├── README.md ├── TestApp ├── Properties │ ├── Settings.settings │ ├── Settings.Designer.cs │ ├── AssemblyInfo.cs │ ├── Resources.Designer.cs │ └── Resources.resx ├── Program.cs ├── Form1.cs ├── TestApp.csproj ├── Form1.resx └── Form1.designer.cs ├── SqlParser ├── FromTag.cs ├── WhereTag.cs ├── SelectTag.cs ├── Properties │ └── AssemblyInfo.cs ├── GroupByTag.cs ├── OrderByTag.cs ├── StartWith.cs ├── ForUpdateTag.cs ├── SqlParser.csproj ├── SimpleTwoWordTag.cs ├── QuotedIdentifierTag.cs ├── BracesTag.cs ├── StringLiteralTag.cs ├── SimpleOneWordTag.cs ├── SqlParser.cs ├── ClassDiagram.cd ├── TagBase.cs └── ParserBase.cs └── SqlParser.sln /README.md: -------------------------------------------------------------------------------- 1 | [![Stories in Ready](https://badge.waffle.io/totpero/SqlParser.png?label=ready&title=Ready)](https://waffle.io/totpero/SqlParser) 2 | # SqlParser 3 | SQL Query Parser projecr form CodeProject (http://www.codeproject.com/Articles/32524/SQL-Parser) 4 | 5 | Test 6 | -------------------------------------------------------------------------------- /TestApp/Properties/Settings.settings: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /TestApp/Program.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Windows.Forms; 4 | 5 | namespace TestApp 6 | { 7 | static class Program 8 | { 9 | /// 10 | /// The main entry point for the application. 11 | /// 12 | [STAThread] 13 | static void Main() 14 | { 15 | Application.EnableVisualStyles(); 16 | Application.SetCompatibleTextRenderingDefault(false); 17 | Application.Run(new Form1()); 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /TestApp/Properties/Settings.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:2.0.50727.1434 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace TestApp.Properties { 12 | 13 | 14 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 15 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "8.0.0.0")] 16 | internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { 17 | 18 | private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); 19 | 20 | public static Settings Default { 21 | get { 22 | return defaultInstance; 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /SqlParser/FromTag.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Parser 6 | { 7 | #region FromTag 8 | 9 | [TagType(FromTag.cTagName)] 10 | [MatchFromTag] 11 | internal class FromTag : SimpleOneWordTag 12 | { 13 | #region Consts 14 | 15 | /// 16 | /// The name of the tag (its identifier). 17 | /// 18 | public const string cTagName = "FROM"; 19 | 20 | #endregion 21 | 22 | #region Properties 23 | 24 | /// 25 | /// Gets the name of the tag (its identifier and sql text) 26 | /// 27 | protected override string Name 28 | { 29 | get 30 | { 31 | return cTagName; 32 | } 33 | } 34 | 35 | #endregion 36 | } 37 | 38 | #endregion 39 | 40 | #region MatchFromTagAttribute 41 | 42 | internal class MatchFromTagAttribute : MatchSimpleOneWordTagAttribute 43 | { 44 | #region Properties 45 | 46 | /// 47 | /// Gets the name of the tag (its identifier and sql text) 48 | /// 49 | protected override string Name 50 | { 51 | get 52 | { 53 | return FromTag.cTagName; 54 | } 55 | } 56 | 57 | #endregion 58 | } 59 | 60 | #endregion 61 | } 62 | -------------------------------------------------------------------------------- /SqlParser/WhereTag.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Parser 6 | { 7 | #region WhereTag 8 | 9 | [TagType(WhereTag.cTagName)] 10 | [MatchWhereTag] 11 | internal class WhereTag : SimpleOneWordTag 12 | { 13 | #region Consts 14 | 15 | /// 16 | /// The name of the tag (its identifier). 17 | /// 18 | public const string cTagName = "WHERE"; 19 | 20 | #endregion 21 | 22 | #region Properties 23 | 24 | /// 25 | /// Gets the name of the tag (its identifier and sql text) 26 | /// 27 | protected override string Name 28 | { 29 | get 30 | { 31 | return cTagName; 32 | } 33 | } 34 | 35 | #endregion 36 | } 37 | 38 | #endregion 39 | 40 | #region MatchWhereTagAttribute 41 | 42 | internal class MatchWhereTagAttribute : MatchSimpleOneWordTagAttribute 43 | { 44 | #region Properties 45 | 46 | /// 47 | /// Gets the name of the tag (its identifier and sql text) 48 | /// 49 | protected override string Name 50 | { 51 | get 52 | { 53 | return WhereTag.cTagName; 54 | } 55 | } 56 | 57 | #endregion 58 | } 59 | 60 | #endregion 61 | } 62 | -------------------------------------------------------------------------------- /SqlParser/SelectTag.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Parser 6 | { 7 | #region SelectTag 8 | 9 | [TagType(SelectTag.cTagName)] 10 | [MatchSelectTag] 11 | internal class SelectTag : SimpleOneWordTag 12 | { 13 | #region Consts 14 | 15 | /// 16 | /// The name of the tag (its identifier). 17 | /// 18 | public const string cTagName = "SELECT"; 19 | 20 | #endregion 21 | 22 | #region Properties 23 | 24 | /// 25 | /// Gets the name of the tag (its identifier and sql text) 26 | /// 27 | protected override string Name 28 | { 29 | get 30 | { 31 | return cTagName; 32 | } 33 | } 34 | 35 | #endregion 36 | } 37 | 38 | #endregion 39 | 40 | #region MatchSelectTagAttribute 41 | 42 | internal class MatchSelectTagAttribute : MatchSimpleOneWordTagAttribute 43 | { 44 | #region Properties 45 | 46 | /// 47 | /// Gets the name of the tag (its identifier and sql text) 48 | /// 49 | protected override string Name 50 | { 51 | get 52 | { 53 | return SelectTag.cTagName; 54 | } 55 | } 56 | 57 | #endregion 58 | } 59 | 60 | #endregion 61 | } 62 | -------------------------------------------------------------------------------- /SqlParser.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 9.00 3 | # Visual Studio 2005 4 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SqlParser", "SqlParser\SqlParser.csproj", "{8A0BB4FD-F01F-4B42-AACB-FC7CD368FEFB}" 5 | EndProject 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestApp", "TestApp\TestApp.csproj", "{7E78BA0E-C411-4F2F-9B9C-110126347D80}" 7 | EndProject 8 | Global 9 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 10 | Debug|Any CPU = Debug|Any CPU 11 | Release|Any CPU = Release|Any CPU 12 | EndGlobalSection 13 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 14 | {8A0BB4FD-F01F-4B42-AACB-FC7CD368FEFB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 15 | {8A0BB4FD-F01F-4B42-AACB-FC7CD368FEFB}.Debug|Any CPU.Build.0 = Debug|Any CPU 16 | {8A0BB4FD-F01F-4B42-AACB-FC7CD368FEFB}.Release|Any CPU.ActiveCfg = Release|Any CPU 17 | {8A0BB4FD-F01F-4B42-AACB-FC7CD368FEFB}.Release|Any CPU.Build.0 = Release|Any CPU 18 | {7E78BA0E-C411-4F2F-9B9C-110126347D80}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 19 | {7E78BA0E-C411-4F2F-9B9C-110126347D80}.Debug|Any CPU.Build.0 = Debug|Any CPU 20 | {7E78BA0E-C411-4F2F-9B9C-110126347D80}.Release|Any CPU.ActiveCfg = Release|Any CPU 21 | {7E78BA0E-C411-4F2F-9B9C-110126347D80}.Release|Any CPU.Build.0 = Release|Any CPU 22 | EndGlobalSection 23 | GlobalSection(SolutionProperties) = preSolution 24 | HideSolutionNode = FALSE 25 | EndGlobalSection 26 | EndGlobal 27 | -------------------------------------------------------------------------------- /TestApp/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("TestApp")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("TestApp")] 13 | [assembly: AssemblyCopyright("Copyright © 2009")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("ff85a389-3158-4ebe-9196-93ab0760fe58")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /SqlParser/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("SqlParser")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("SqlParser")] 13 | [assembly: AssemblyCopyright("Copyright © 2009")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("7c084735-40a7-4fc6-8906-9845c75a79d6")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Build and Revision Numbers 33 | // by using the '*' as shown below: 34 | // [assembly: AssemblyVersion("1.0.*")] 35 | [assembly: AssemblyVersion("1.0.0.0")] 36 | [assembly: AssemblyFileVersion("1.0.0.0")] 37 | -------------------------------------------------------------------------------- /TestApp/Form1.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Data; 5 | using System.Drawing; 6 | using System.Text; 7 | using System.Windows.Forms; 8 | using Parser; 9 | 10 | namespace TestApp 11 | { 12 | public partial class Form1 : Form 13 | { 14 | public Form1() 15 | { 16 | InitializeComponent(); 17 | } 18 | 19 | private void fButtonModify_Click(object sender, EventArgs e) 20 | { 21 | if (string.IsNullOrEmpty(fTextBoxOriginal.Text.Trim())) 22 | { 23 | fTextBoxFinal.Text = string.Empty; 24 | return; 25 | } 26 | 27 | try 28 | { 29 | SqlParser myParser = new SqlParser(); 30 | myParser.Parse(fTextBoxOriginal.Text); 31 | 32 | if (!string.IsNullOrEmpty(fTextBoxWhereClause.Text.Trim())) 33 | { 34 | string myOrginalWhereClause = myParser.WhereClause; 35 | if (string.IsNullOrEmpty(myOrginalWhereClause)) 36 | myParser.WhereClause = fTextBoxWhereClause.Text; 37 | else 38 | myParser.WhereClause = string.Format("({0}) AND ({1})", myOrginalWhereClause, fTextBoxWhereClause.Text); 39 | } 40 | 41 | if (!string.IsNullOrEmpty(fTextBoxOrderBy.Text.Trim())) 42 | { 43 | string myOrginalOrderByClause = myParser.OrderByClause; 44 | 45 | if (string.IsNullOrEmpty(myOrginalOrderByClause)) 46 | myParser.OrderByClause = fTextBoxOrderBy.Text; 47 | else 48 | myParser.OrderByClause = string.Format("{0}, {1}", myOrginalOrderByClause, fTextBoxOrderBy.Text); 49 | } 50 | 51 | fTextBoxFinal.Text = myParser.ToText(); 52 | } 53 | catch (Exception myEx) 54 | { 55 | MessageBox.Show(myEx.Message); 56 | } 57 | } 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /SqlParser/GroupByTag.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Parser 6 | { 7 | #region GroupByTag 8 | 9 | [TagType(GroupByTag.cTagName)] 10 | [MatchGroupByTag] 11 | internal class GroupByTag : SimpleTwoWordTag 12 | { 13 | #region Consts 14 | 15 | /// 16 | /// The name of the tag (its identifier). 17 | /// 18 | public const string cTagName = "GROUP_BY"; 19 | 20 | /// 21 | /// The first part of tag. 22 | /// 23 | public const string cTagFirstPart = "GROUP"; 24 | 25 | /// 26 | /// The second part of tag. 27 | /// 28 | public const string cTagSecondPart = "BY"; 29 | 30 | #endregion 31 | 32 | #region Properties 33 | 34 | /// 35 | /// Gets the name of the tag. 36 | /// 37 | protected override string Name 38 | { 39 | get 40 | { 41 | return cTagName; 42 | } 43 | } 44 | 45 | /// 46 | /// Gets the first word of the tag. 47 | /// 48 | protected override string FirstWord 49 | { 50 | get 51 | { 52 | return cTagFirstPart; 53 | } 54 | } 55 | 56 | /// 57 | /// Gets the second word of the tag. 58 | /// 59 | protected override string SecondWord 60 | { 61 | get 62 | { 63 | return cTagSecondPart; 64 | } 65 | } 66 | 67 | #endregion 68 | } 69 | 70 | #endregion 71 | 72 | #region MatchGroupByTagAttribute 73 | 74 | internal class MatchGroupByTagAttribute : MatchSimpleTwoWordTagAttribute 75 | { 76 | #region Properties 77 | 78 | /// 79 | /// Gets the name of the tag (its identifier and sql text) 80 | /// 81 | protected override string Name 82 | { 83 | get 84 | { 85 | return GroupByTag.cTagName; 86 | } 87 | } 88 | 89 | /// 90 | /// Gets the first word of the tag. 91 | /// 92 | protected override string FirstWord 93 | { 94 | get 95 | { 96 | return GroupByTag.cTagFirstPart; 97 | } 98 | } 99 | 100 | /// 101 | /// Gets the second word of the tag. 102 | /// 103 | protected override string SecondWord 104 | { 105 | get 106 | { 107 | return GroupByTag.cTagSecondPart; 108 | } 109 | } 110 | 111 | #endregion 112 | } 113 | 114 | #endregion 115 | } 116 | -------------------------------------------------------------------------------- /SqlParser/OrderByTag.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Parser 6 | { 7 | #region OrderByTag 8 | 9 | [TagType(OrderByTag.cTagName)] 10 | [MatchOrderByTag] 11 | internal class OrderByTag : SimpleTwoWordTag 12 | { 13 | #region Consts 14 | 15 | /// 16 | /// The name of the tag (its identifier). 17 | /// 18 | public const string cTagName = "ORDER_BY"; 19 | 20 | /// 21 | /// The first part of tag. 22 | /// 23 | public const string cTagFirstPart = "ORDER"; 24 | 25 | /// 26 | /// The second part of tag. 27 | /// 28 | public const string cTagSecondPart = "BY"; 29 | 30 | #endregion 31 | 32 | #region Properties 33 | 34 | /// 35 | /// Gets the name of the tag. 36 | /// 37 | protected override string Name 38 | { 39 | get 40 | { 41 | return cTagName; 42 | } 43 | } 44 | 45 | /// 46 | /// Gets the first word of the tag. 47 | /// 48 | protected override string FirstWord 49 | { 50 | get 51 | { 52 | return cTagFirstPart; 53 | } 54 | } 55 | 56 | /// 57 | /// Gets the second word of the tag. 58 | /// 59 | protected override string SecondWord 60 | { 61 | get 62 | { 63 | return cTagSecondPart; 64 | } 65 | } 66 | 67 | #endregion 68 | } 69 | 70 | #endregion 71 | 72 | #region MatchOrderByTagAttribute 73 | 74 | internal class MatchOrderByTagAttribute : MatchSimpleTwoWordTagAttribute 75 | { 76 | #region Properties 77 | 78 | /// 79 | /// Gets the name of the tag (its identifier and sql text) 80 | /// 81 | protected override string Name 82 | { 83 | get 84 | { 85 | return OrderByTag.cTagName; 86 | } 87 | } 88 | 89 | /// 90 | /// Gets the first word of the tag. 91 | /// 92 | protected override string FirstWord 93 | { 94 | get 95 | { 96 | return OrderByTag.cTagFirstPart; 97 | } 98 | } 99 | 100 | /// 101 | /// Gets the second word of the tag. 102 | /// 103 | protected override string SecondWord 104 | { 105 | get 106 | { 107 | return OrderByTag.cTagSecondPart; 108 | } 109 | } 110 | 111 | #endregion 112 | } 113 | 114 | #endregion 115 | } 116 | -------------------------------------------------------------------------------- /SqlParser/StartWith.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Parser 6 | { 7 | #region StartWith 8 | 9 | [TagType(StartWith.cTagName)] 10 | [MatchStartWithTag] 11 | internal class StartWith : SimpleTwoWordTag 12 | { 13 | #region Consts 14 | 15 | /// 16 | /// The name of the tag (its identifier). 17 | /// 18 | public const string cTagName = "START_WITH"; 19 | 20 | /// 21 | /// The first part of tag. 22 | /// 23 | public const string cTagFirstPart = "START"; 24 | 25 | /// 26 | /// The second part of tag. 27 | /// 28 | public const string cTagSecondPart = "WITH"; 29 | 30 | #endregion 31 | 32 | #region Properties 33 | 34 | /// 35 | /// Gets the name of the tag. 36 | /// 37 | protected override string Name 38 | { 39 | get 40 | { 41 | return cTagName; 42 | } 43 | } 44 | 45 | /// 46 | /// Gets the first word of the tag. 47 | /// 48 | protected override string FirstWord 49 | { 50 | get 51 | { 52 | return cTagFirstPart; 53 | } 54 | } 55 | 56 | /// 57 | /// Gets the second word of the tag. 58 | /// 59 | protected override string SecondWord 60 | { 61 | get 62 | { 63 | return cTagSecondPart; 64 | } 65 | } 66 | 67 | #endregion 68 | } 69 | 70 | #endregion 71 | 72 | #region MatchOrderByTagAttribute 73 | 74 | internal class MatchStartWithTagAttribute : MatchSimpleTwoWordTagAttribute 75 | { 76 | #region Properties 77 | 78 | /// 79 | /// Gets the name of the tag (its identifier and sql text) 80 | /// 81 | protected override string Name 82 | { 83 | get 84 | { 85 | return StartWith.cTagName; 86 | } 87 | } 88 | 89 | /// 90 | /// Gets the first word of the tag. 91 | /// 92 | protected override string FirstWord 93 | { 94 | get 95 | { 96 | return StartWith.cTagFirstPart; 97 | } 98 | } 99 | 100 | /// 101 | /// Gets the second word of the tag. 102 | /// 103 | protected override string SecondWord 104 | { 105 | get 106 | { 107 | return StartWith.cTagSecondPart; 108 | } 109 | } 110 | 111 | #endregion 112 | } 113 | 114 | #endregion 115 | } 116 | -------------------------------------------------------------------------------- /SqlParser/ForUpdateTag.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Parser 6 | { 7 | #region ForUpdateTag 8 | 9 | [TagType(ForUpdateTag.cTagName)] 10 | [MatchForUpdateTag] 11 | internal class ForUpdateTag : SimpleTwoWordTag 12 | { 13 | #region Consts 14 | 15 | /// 16 | /// The name of the tag (its identifier). 17 | /// 18 | public const string cTagName = "FOR_UPDATE"; 19 | 20 | /// 21 | /// The first part of tag. 22 | /// 23 | public const string cTagFirstPart = "FOR"; 24 | 25 | /// 26 | /// The second part of tag. 27 | /// 28 | public const string cTagSecondPart = "UPDATE"; 29 | 30 | #endregion 31 | 32 | #region Properties 33 | 34 | /// 35 | /// Gets the name of the tag. 36 | /// 37 | protected override string Name 38 | { 39 | get 40 | { 41 | return cTagName; 42 | } 43 | } 44 | 45 | /// 46 | /// Gets the first word of the tag. 47 | /// 48 | protected override string FirstWord 49 | { 50 | get 51 | { 52 | return cTagFirstPart; 53 | } 54 | } 55 | 56 | /// 57 | /// Gets the second word of the tag. 58 | /// 59 | protected override string SecondWord 60 | { 61 | get 62 | { 63 | return cTagSecondPart; 64 | } 65 | } 66 | 67 | #endregion 68 | } 69 | 70 | #endregion 71 | 72 | #region MatchForUpdateTagAttribute 73 | 74 | internal class MatchForUpdateTagAttribute : MatchSimpleTwoWordTagAttribute 75 | { 76 | #region Properties 77 | 78 | /// 79 | /// Gets the name of the tag (its identifier and sql text) 80 | /// 81 | protected override string Name 82 | { 83 | get 84 | { 85 | return ForUpdateTag.cTagName; 86 | } 87 | } 88 | 89 | /// 90 | /// Gets the first word of the tag. 91 | /// 92 | protected override string FirstWord 93 | { 94 | get 95 | { 96 | return ForUpdateTag.cTagFirstPart; 97 | } 98 | } 99 | 100 | /// 101 | /// Gets the second word of the tag. 102 | /// 103 | protected override string SecondWord 104 | { 105 | get 106 | { 107 | return ForUpdateTag.cTagSecondPart; 108 | } 109 | } 110 | 111 | #endregion 112 | } 113 | 114 | #endregion 115 | } 116 | -------------------------------------------------------------------------------- /SqlParser/SqlParser.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Debug 4 | AnyCPU 5 | 8.0.50727 6 | 2.0 7 | {8A0BB4FD-F01F-4B42-AACB-FC7CD368FEFB} 8 | Library 9 | Properties 10 | SqlParser 11 | SqlParser 12 | 13 | 14 | true 15 | full 16 | false 17 | bin\Debug\ 18 | DEBUG;TRACE 19 | prompt 20 | 4 21 | 22 | 23 | pdbonly 24 | true 25 | bin\Release\ 26 | TRACE 27 | prompt 28 | 4 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 64 | -------------------------------------------------------------------------------- /TestApp/Properties/Resources.Designer.cs: -------------------------------------------------------------------------------- 1 | //------------------------------------------------------------------------------ 2 | // 3 | // This code was generated by a tool. 4 | // Runtime Version:2.0.50727.1434 5 | // 6 | // Changes to this file may cause incorrect behavior and will be lost if 7 | // the code is regenerated. 8 | // 9 | //------------------------------------------------------------------------------ 10 | 11 | namespace TestApp.Properties { 12 | using System; 13 | 14 | 15 | /// 16 | /// A strongly-typed resource class, for looking up localized strings, etc. 17 | /// 18 | // This class was auto-generated by the StronglyTypedResourceBuilder 19 | // class via a tool like ResGen or Visual Studio. 20 | // To add or remove a member, edit your .ResX file then rerun ResGen 21 | // with the /str option, or rebuild your VS project. 22 | [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] 23 | [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] 24 | [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] 25 | internal class Resources { 26 | 27 | private static global::System.Resources.ResourceManager resourceMan; 28 | 29 | private static global::System.Globalization.CultureInfo resourceCulture; 30 | 31 | [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] 32 | internal Resources() { 33 | } 34 | 35 | /// 36 | /// Returns the cached ResourceManager instance used by this class. 37 | /// 38 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 39 | internal static global::System.Resources.ResourceManager ResourceManager { 40 | get { 41 | if (object.ReferenceEquals(resourceMan, null)) { 42 | global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("TestApp.Properties.Resources", typeof(Resources).Assembly); 43 | resourceMan = temp; 44 | } 45 | return resourceMan; 46 | } 47 | } 48 | 49 | /// 50 | /// Overrides the current thread's CurrentUICulture property for all 51 | /// resource lookups using this strongly typed resource class. 52 | /// 53 | [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] 54 | internal static global::System.Globalization.CultureInfo Culture { 55 | get { 56 | return resourceCulture; 57 | } 58 | set { 59 | resourceCulture = value; 60 | } 61 | } 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /SqlParser/SimpleTwoWordTag.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Parser 6 | { 7 | #region SimpleTwoWordTag 8 | 9 | /// 10 | /// The base class for such tags as Order By, Group By etc. 11 | /// 12 | internal abstract class SimpleTwoWordTag : SimpleOneWordTag 13 | { 14 | #region Methods 15 | 16 | #region Common 17 | 18 | /// 19 | /// Checks whether there is the tag at the specified position 20 | /// in the specified sql. 21 | /// 22 | /// 23 | /// The position after the tag or -1 there is no tag at the position. 24 | /// 25 | protected override int MatchStart(string sql, int position) 26 | { 27 | return MatchStart(FirstWord, SecondWord, sql, position); 28 | } 29 | 30 | /// 31 | /// Writes the start of the tag. 32 | /// 33 | public override void WriteStart(StringBuilder output) 34 | { 35 | CheckInitialized(); 36 | 37 | #region Check the parameters 38 | 39 | if (output == null) 40 | throw new ArgumentNullException(); 41 | 42 | #endregion 43 | 44 | output.Append(FirstWord); 45 | output.Append(ParserBase.cWhiteSpace); 46 | output.Append(SecondWord); 47 | } 48 | 49 | #endregion 50 | 51 | #region Static 52 | 53 | /// 54 | /// Checks whether there is the tag at the specified position 55 | /// in the specified sql. 56 | /// 57 | /// The value of the Name property. 58 | /// 59 | /// The position after the tag or -1 there is no tag at the position. 60 | /// 61 | internal static int MatchStart(string firstWord, string secondWord, string sql, int position) 62 | { 63 | #region Check the arguments 64 | 65 | ParserBase.CheckTextAndPositionArguments(sql, position); 66 | 67 | #endregion 68 | 69 | if (string.Compare(sql, position, firstWord, 0, firstWord.Length, true) != 0) 70 | return -1; 71 | 72 | position += firstWord.Length; 73 | 74 | ParserBase.SkipWhiteSpace(sql, ref position); 75 | if (position == sql.Length) 76 | return -1; 77 | 78 | if (string.Compare(sql, position, secondWord, 0, secondWord.Length, true) != 0) 79 | return -1; 80 | 81 | return position + secondWord.Length; 82 | } 83 | 84 | #endregion 85 | 86 | #endregion 87 | 88 | #region Properties 89 | 90 | /// 91 | /// Gets the first word of the tag. 92 | /// 93 | protected abstract string FirstWord { get; } 94 | 95 | /// 96 | /// Gets the second word of the tag. 97 | /// 98 | protected abstract string SecondWord { get; } 99 | 100 | #endregion 101 | } 102 | 103 | #endregion 104 | 105 | #region MatchSimpleTwoWordTagAttribute 106 | 107 | internal abstract class MatchSimpleTwoWordTagAttribute : MatchSimpleOneWordTagAttribute 108 | { 109 | #region Methods 110 | 111 | public override bool Match(string sql, int position) 112 | { 113 | return SimpleTwoWordTag.MatchStart(FirstWord, SecondWord, sql, position) >= 0; 114 | } 115 | 116 | #endregion 117 | 118 | #region Properties 119 | 120 | /// 121 | /// Gets the first word of the tag. 122 | /// 123 | protected abstract string FirstWord { get; } 124 | 125 | /// 126 | /// Gets the second word of the tag. 127 | /// 128 | protected abstract string SecondWord { get; } 129 | 130 | #endregion 131 | } 132 | 133 | #endregion 134 | } 135 | -------------------------------------------------------------------------------- /SqlParser/QuotedIdentifierTag.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Parser 6 | { 7 | #region QuotedIdentifierTag 8 | 9 | [TagType(QuotedIdentifierTag.cTagName)] 10 | [MatchQuotedIdentifierTag] 11 | internal class QuotedIdentifierTag : TagBase 12 | { 13 | #region Consts 14 | 15 | /// 16 | /// The name of the tag (its identifier). 17 | /// 18 | public const string cTagName = "QUOTED_IDENTIFIER"; 19 | 20 | /// 21 | /// The tag delimiter. 22 | /// 23 | public const string cTagDelimiter = "\""; 24 | 25 | #endregion 26 | 27 | #region Methods 28 | 29 | #region Common 30 | 31 | /// 32 | /// Reads the tag at the specified position in the specified word and separator array. 33 | /// 34 | /// 35 | /// The position after the tag (at which to continue reading). 36 | /// 37 | protected override int InitializeCoreFromText(ParserBase parser, string sql, int position, TagBase parentTag) 38 | { 39 | #region Check the arguments 40 | 41 | ParserBase.CheckTextAndPositionArguments(sql, position); 42 | 43 | #endregion 44 | 45 | int myAfterTagStartPos = MatchStartStatic(sql, position); 46 | 47 | if (myAfterTagStartPos < 0) 48 | throw new Exception("Cannot read the QuotedIdentifier tag."); 49 | 50 | #region Read the identifier's value 51 | 52 | int myTagEndPos = sql.IndexOf(cTagDelimiter, myAfterTagStartPos, StringComparison.InvariantCultureIgnoreCase); 53 | if (myTagEndPos < 0) 54 | throw new Exception("Cannot read the QuotedIdentifier tag."); 55 | 56 | if (myAfterTagStartPos == myTagEndPos) 57 | Value = string.Empty; 58 | else 59 | Value = sql.Substring(myAfterTagStartPos, myTagEndPos - myAfterTagStartPos); 60 | 61 | #endregion 62 | 63 | Parser = parser; 64 | 65 | HasContents = false; 66 | 67 | return myTagEndPos + cTagDelimiter.Length; 68 | } 69 | 70 | /// 71 | /// Writes the start of the tag. 72 | /// 73 | public override void WriteStart(StringBuilder output) 74 | { 75 | CheckInitialized(); 76 | 77 | #region Check the parameters 78 | 79 | if (output == null) 80 | throw new ArgumentNullException(); 81 | 82 | #endregion 83 | 84 | output.Append(cTagDelimiter); 85 | output.Append(Value); 86 | output.Append(cTagDelimiter); 87 | } 88 | 89 | #endregion 90 | 91 | #region Static 92 | 93 | /// 94 | /// Checks whether there is the tag start at the specified position 95 | /// in the specified sql. 96 | /// 97 | /// 98 | /// The position after the tag or -1 there is no tag start at the position. 99 | /// 100 | public static int MatchStartStatic(string sql, int position) 101 | { 102 | #region Check the arguments 103 | 104 | ParserBase.CheckTextAndPositionArguments(sql, position); 105 | 106 | #endregion 107 | 108 | if (string.Compare(sql, position, cTagDelimiter, 0, cTagDelimiter.Length, true) != 0) 109 | return -1; 110 | 111 | return position + cTagDelimiter.Length; 112 | } 113 | 114 | #endregion 115 | 116 | #endregion 117 | } 118 | 119 | #endregion 120 | 121 | #region MatchQuotedIdentifierTagAttribute 122 | 123 | internal class MatchQuotedIdentifierTagAttribute : MatchTagAttributeBase 124 | { 125 | #region Methods 126 | 127 | public override bool Match(string sql, int position) 128 | { 129 | return QuotedIdentifierTag.MatchStartStatic(sql, position) >= 0; 130 | } 131 | 132 | #endregion 133 | } 134 | 135 | #endregion 136 | } 137 | -------------------------------------------------------------------------------- /TestApp/TestApp.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | Debug 4 | AnyCPU 5 | 8.0.50727 6 | 2.0 7 | {7E78BA0E-C411-4F2F-9B9C-110126347D80} 8 | WinExe 9 | Properties 10 | TestApp 11 | TestApp 12 | 13 | 14 | true 15 | full 16 | false 17 | bin\Debug\ 18 | DEBUG;TRACE 19 | prompt 20 | 4 21 | 22 | 23 | pdbonly 24 | true 25 | bin\Release\ 26 | TRACE 27 | prompt 28 | 4 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | Form1.cs 41 | Designer 42 | 43 | 44 | ResXFileCodeGenerator 45 | Resources.Designer.cs 46 | Designer 47 | 48 | 49 | 50 | Form 51 | 52 | 53 | Form1.cs 54 | 55 | 56 | 57 | True 58 | Resources.resx 59 | True 60 | 61 | 62 | SettingsSingleFileGenerator 63 | Settings.Designer.cs 64 | 65 | 66 | True 67 | Settings.settings 68 | True 69 | 70 | 71 | 72 | 73 | {8A0BB4FD-F01F-4B42-AACB-FC7CD368FEFB} 74 | SqlParser 75 | 76 | 77 | 78 | 85 | -------------------------------------------------------------------------------- /SqlParser/BracesTag.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Parser 6 | { 7 | #region BracesTag 8 | 9 | [TagType(BracesTag.cTagName)] 10 | [MatchBracesTag] 11 | internal class BracesTag : TagBase 12 | { 13 | #region Consts 14 | 15 | /// 16 | /// The name of the tag (its identifier). 17 | /// 18 | public const string cTagName = "BRACES"; 19 | 20 | /// 21 | /// The start of the tag. 22 | /// 23 | public const string cStartTag = "("; 24 | 25 | /// 26 | /// The end of the tag. 27 | /// 28 | public const string cEndTag = ")"; 29 | 30 | #endregion 31 | 32 | #region Methods 33 | 34 | #region Common 35 | 36 | /// 37 | /// Reads the tag at the specified position in the specified word and separator array. 38 | /// 39 | /// 40 | /// The position after the tag (at which to continue reading). 41 | /// 42 | protected override int InitializeCoreFromText(ParserBase parser, string sql, int position, TagBase parentTag) 43 | { 44 | #region Check the arguments 45 | 46 | ParserBase.CheckTextAndPositionArguments(sql, position); 47 | 48 | #endregion 49 | 50 | int myResult = MatchStartStatic(sql, position); 51 | 52 | if (myResult < 0) 53 | throw new Exception("Cannot read the Braces tag."); 54 | 55 | Parser = parser; 56 | 57 | HasContents = true; 58 | 59 | return myResult; 60 | } 61 | 62 | /// 63 | /// Returns a value indicating whether there is the tag ending at the specified position. /// 64 | /// 65 | /// 66 | /// If this value is less than zero, then there is no ending; otherwise the 67 | /// position after ending is returned. 68 | /// 69 | public override int MatchEnd(string sql, int position) 70 | { 71 | CheckInitialized(); 72 | 73 | #region Check the arguments 74 | 75 | ParserBase.CheckTextAndPositionArguments(sql, position); 76 | 77 | #endregion 78 | 79 | return MatchEndStatic(sql, position); 80 | } 81 | 82 | /// 83 | /// Writes the start of the tag. 84 | /// 85 | public override void WriteStart(StringBuilder output) 86 | { 87 | CheckInitialized(); 88 | 89 | #region Check the parameters 90 | 91 | if (output == null) 92 | throw new ArgumentNullException(); 93 | 94 | #endregion 95 | 96 | output.Append(cStartTag); 97 | } 98 | 99 | /// 100 | /// Writes the end of the tag. 101 | /// 102 | public override void WriteEnd(StringBuilder output) 103 | { 104 | CheckInitialized(); 105 | 106 | #region Check the parameters 107 | 108 | if (output == null) 109 | throw new ArgumentNullException(); 110 | 111 | #endregion 112 | 113 | output.Append(cEndTag); 114 | } 115 | 116 | #endregion 117 | 118 | #region Static 119 | 120 | /// 121 | /// Checks whether there is the tag start at the specified position 122 | /// in the specified sql. 123 | /// 124 | /// 125 | /// The position after the tag or -1 there is no tag start at the position. 126 | /// 127 | public static int MatchStartStatic(string sql, int position) 128 | { 129 | #region Check the arguments 130 | 131 | ParserBase.CheckTextAndPositionArguments(sql, position); 132 | 133 | #endregion 134 | 135 | if (string.Compare(sql, position, cStartTag, 0, cStartTag.Length, true) != 0) 136 | return -1; 137 | 138 | return position + cStartTag.Length; 139 | } 140 | 141 | /// 142 | /// Checks whether there is the tag end at the specified position 143 | /// in the specified sql. 144 | /// 145 | /// 146 | /// The position after the tag or -1 there is no tag end at the position. 147 | /// 148 | public static int MatchEndStatic(string sql, int position) 149 | { 150 | #region Check the arguments 151 | 152 | ParserBase.CheckTextAndPositionArguments(sql, position); 153 | 154 | #endregion 155 | 156 | if (string.Compare(sql, position, cEndTag, 0, cEndTag.Length, true) != 0) 157 | return -1; 158 | 159 | return position + cEndTag.Length; 160 | } 161 | 162 | #endregion 163 | 164 | #endregion 165 | } 166 | 167 | #endregion 168 | 169 | #region MatchBracesTagAttribute 170 | 171 | internal class MatchBracesTagAttribute : MatchTagAttributeBase 172 | { 173 | #region Methods 174 | 175 | public override bool Match(string sql, int position) 176 | { 177 | return BracesTag.MatchStartStatic(sql, position) >= 0; 178 | } 179 | 180 | #endregion 181 | } 182 | 183 | #endregion 184 | } 185 | -------------------------------------------------------------------------------- /TestApp/Properties/Resources.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | text/microsoft-resx 107 | 108 | 109 | 2.0 110 | 111 | 112 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 113 | 114 | 115 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | -------------------------------------------------------------------------------- /SqlParser/StringLiteralTag.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Parser 6 | { 7 | #region StringLiteralTag 8 | 9 | [TagType(StringLiteralTag.cTagName)] 10 | [MatchStringLiteralTag] 11 | internal class StringLiteralTag : TagBase 12 | { 13 | #region Consts 14 | 15 | /// 16 | /// The name of the tag (its identifier). 17 | /// 18 | public const string cTagName = "STRING_LITERAL"; 19 | 20 | /// 21 | /// The tag. 22 | /// 23 | public const string cTagDelimiter = "'"; 24 | 25 | #endregion 26 | 27 | #region Methods 28 | 29 | #region Common 30 | 31 | /// 32 | /// Reads the tag at the specified position in the specified sql string. 33 | /// 34 | /// 35 | /// The position after the tag (at which to continue reading). 36 | /// 37 | protected override int InitializeCoreFromText(ParserBase parser, string sql, int position, TagBase parentTag) 38 | { 39 | #region Check the arguments 40 | 41 | ParserBase.CheckTextAndPositionArguments(sql, position); 42 | 43 | #endregion 44 | 45 | int myLiteralStartPos = position; 46 | 47 | position = MatchStartStatic(sql, position); 48 | 49 | if (position < 0) 50 | throw new Exception("Cannot read the StringLiteral tag."); 51 | 52 | #region Read the literal value 53 | 54 | int myResult = -1; 55 | int myValueStartPos = position; 56 | while (position < sql.Length) 57 | { 58 | myResult = MatchEnd(sql, position, myLiteralStartPos); 59 | if (myResult >= 0) 60 | { 61 | if (position == myValueStartPos) 62 | Value = string.Empty; 63 | else 64 | Value = sql.Substring(myValueStartPos, position - myValueStartPos); 65 | break; 66 | } 67 | position++; 68 | } 69 | 70 | if (myResult < 0) 71 | throw new Exception("Cannot read the StringLiteral tag."); 72 | 73 | #endregion 74 | 75 | Parser = parser; 76 | 77 | HasContents = false; 78 | 79 | return myResult; 80 | } 81 | 82 | /// 83 | /// Returns a value indicating whether there is the tag ending at the specified position. 84 | /// 85 | /// 86 | /// If this value is less than zero, then there is no ending; otherwise the 87 | /// position after ending is returned. 88 | /// 89 | private int MatchEnd(string sql, int position, int literalStartPos) 90 | { 91 | #region Check the arguments 92 | 93 | ParserBase.CheckTextAndPositionArguments(sql, position); 94 | 95 | #endregion 96 | 97 | if (string.Compare(sql, position, cTagDelimiter, 0, cTagDelimiter.Length, true) != 0) 98 | return -1; 99 | 100 | #region Check the next and previous symbols (where there are ' symbols) 101 | 102 | #region Determine the number of ' symbols before 103 | 104 | int myNumberOfApostBefore = 0; 105 | for (int myCurPos = position - 1; myCurPos >= literalStartPos; myCurPos--) 106 | { 107 | if (string.Compare(sql, myCurPos, cTagDelimiter, 0, cTagDelimiter.Length, true) == 0) 108 | myNumberOfApostBefore++; 109 | else 110 | break; 111 | } 112 | 113 | #endregion 114 | 115 | if (!(myNumberOfApostBefore == 1 && position == literalStartPos + 1)) 116 | { 117 | if (myNumberOfApostBefore % 2 == 1) 118 | return -1; 119 | 120 | #region Check whether the next symbol is ' 121 | 122 | if (position + 1 < sql.Length) 123 | { 124 | if (string.Compare(sql, position + 1, cTagDelimiter, 0, cTagDelimiter.Length, true) == 0) 125 | return -1; 126 | } 127 | 128 | #endregion 129 | } 130 | 131 | #endregion 132 | 133 | return position + cTagDelimiter.Length; 134 | } 135 | 136 | /// 137 | /// Writes the start of the tag. 138 | /// 139 | public override void WriteStart(StringBuilder output) 140 | { 141 | CheckInitialized(); 142 | 143 | #region Check the parameters 144 | 145 | if (output == null) 146 | throw new ArgumentNullException(); 147 | 148 | #endregion 149 | 150 | output.Append(cTagDelimiter); 151 | output.Append(Value); 152 | WriteEnd(output); 153 | } 154 | 155 | /// 156 | /// Writes the end of the tag. 157 | /// 158 | private new void WriteEnd(StringBuilder output) 159 | { 160 | CheckInitialized(); 161 | 162 | #region Check the parameters 163 | 164 | if (output == null) 165 | throw new ArgumentNullException(); 166 | 167 | #endregion 168 | 169 | output.Append(cTagDelimiter); 170 | } 171 | 172 | #endregion 173 | 174 | #region Static 175 | 176 | /// 177 | /// Checks whether there is the tag start at the specified position 178 | /// in the specified sql. 179 | /// 180 | /// 181 | /// The position after the tag or -1 there is no tag start at the position. 182 | /// 183 | public static int MatchStartStatic(string sql, int position) 184 | { 185 | #region Check the arguments 186 | 187 | ParserBase.CheckTextAndPositionArguments(sql, position); 188 | 189 | #endregion 190 | 191 | if (string.Compare(sql, position, cTagDelimiter, 0, cTagDelimiter.Length, true) != 0) 192 | return -1; 193 | 194 | return position + cTagDelimiter.Length; 195 | } 196 | 197 | #endregion 198 | 199 | #endregion 200 | } 201 | 202 | #endregion 203 | 204 | #region MatchStringLiteralTagAttribute 205 | 206 | internal class MatchStringLiteralTagAttribute : MatchTagAttributeBase 207 | { 208 | #region Methods 209 | 210 | public override bool Match(string sql, int position) 211 | { 212 | return StringLiteralTag.MatchStartStatic(sql, position) >= 0; 213 | } 214 | 215 | #endregion 216 | } 217 | 218 | #endregion 219 | } 220 | -------------------------------------------------------------------------------- /SqlParser/SimpleOneWordTag.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Parser 6 | { 7 | #region SimpleOneWordTag 8 | 9 | /// 10 | /// The base class for such tags as Select, From etc. 11 | /// 12 | internal abstract class SimpleOneWordTag: TagBase 13 | { 14 | #region Fields 15 | 16 | /// 17 | /// The value of the ParentTag property. 18 | /// 19 | private TagBase fParentTag; 20 | 21 | #endregion 22 | 23 | #region Methods 24 | 25 | #region Common 26 | 27 | /// 28 | /// Reads the tag at the specified position in the specified sql. 29 | /// 30 | /// 31 | /// The position after the tag (at which to continue reading). 32 | /// 33 | protected override int InitializeCoreFromText(ParserBase parser, string sql, int position, TagBase parentTag) 34 | { 35 | #region Check the arguments 36 | 37 | ParserBase.CheckTextAndPositionArguments(sql, position); 38 | 39 | #endregion 40 | 41 | int myResult = MatchStart(sql, position); 42 | 43 | if (myResult < 0) 44 | throw new Exception(string.Format("Cannot read the {0} tag.", Name)); 45 | 46 | Parser = parser; 47 | 48 | HasContents = true; 49 | 50 | ParentTag = parentTag; 51 | 52 | return myResult; 53 | } 54 | 55 | /// 56 | /// Checks whether there is the tag at the specified position 57 | /// in the specified sql. 58 | /// 59 | /// 60 | /// The position after the tag or -1 there is no tag at the position. 61 | /// 62 | protected virtual int MatchStart(string sql, int position) 63 | { 64 | return MatchStart(Name, sql, position); 65 | } 66 | 67 | /// 68 | /// Writes the start of the tag. 69 | /// 70 | public override void WriteStart(StringBuilder output) 71 | { 72 | CheckInitialized(); 73 | 74 | #region Check the parameters 75 | 76 | if (output == null) 77 | throw new ArgumentNullException(); 78 | 79 | #endregion 80 | 81 | output.Append(Name); 82 | } 83 | 84 | /// 85 | /// Writes the end of the tag. 86 | /// 87 | public override void WriteEnd(StringBuilder output) 88 | { 89 | CheckInitialized(); 90 | 91 | #region Check the parameters 92 | 93 | if (output == null) 94 | throw new ArgumentNullException(); 95 | 96 | #endregion 97 | 98 | return; 99 | } 100 | 101 | /// 102 | /// Returns a value indicating whether there is the tag ending at the specified position. 103 | /// 104 | /// 105 | /// If this value is less than zero, then there is no ending; otherwise the 106 | /// position after ending is returned. 107 | /// 108 | public override int MatchEnd(string sql, int position) 109 | { 110 | CheckInitialized(); 111 | 112 | #region Check the arguments 113 | 114 | ParserBase.CheckTextAndPositionArguments(sql, position); 115 | 116 | #endregion 117 | 118 | if (ParentTag != null && ParentTag.MatchEnd(sql, position) >= 0) 119 | return position; 120 | 121 | Type myTag = (Parser as SqlParser).IsTag(sql, position); 122 | if ( 123 | myTag != null && 124 | !myTag.IsAssignableFrom(typeof(StringLiteralTag)) && 125 | !myTag.IsAssignableFrom(typeof(BracesTag)) 126 | ) 127 | return position; 128 | 129 | return -1; 130 | } 131 | 132 | #endregion 133 | 134 | #region Static 135 | 136 | /// 137 | /// Checks whether there is the tag at the specified position 138 | /// in the specified sql. 139 | /// 140 | /// The value of the Name property. 141 | /// 142 | /// The position after the tag or -1 there is no tag at the position. 143 | /// 144 | internal static int MatchStart(string name, string sql, int position) 145 | { 146 | #region Check the arguments 147 | 148 | if (name == null) 149 | throw new ArgumentNullException("name"); 150 | 151 | ParserBase.CheckTextAndPositionArguments(sql, position); 152 | 153 | #endregion 154 | 155 | if (string.Compare(sql, position, name, 0, name.Length, true) != 0) 156 | return -1; 157 | 158 | return position + name.Length; 159 | } 160 | 161 | #endregion 162 | 163 | #endregion 164 | 165 | #region Properties 166 | 167 | /// 168 | /// Gets the name of the tag (its identifier and sql text) 169 | /// 170 | protected abstract string Name { get; } 171 | 172 | /// 173 | /// Inidicates whether the string end can be treated as the end of the tag. 174 | /// 175 | public override bool CanTerminateByStringEnd 176 | { 177 | get 178 | { 179 | CheckInitialized(); 180 | 181 | return true; 182 | } 183 | } 184 | 185 | /// 186 | /// Returns the parent tag of this tag in the sql being parsed. 187 | /// 188 | public TagBase ParentTag 189 | { 190 | get 191 | { 192 | return fParentTag; 193 | } 194 | private set 195 | { 196 | fParentTag = value; 197 | } 198 | } 199 | 200 | #endregion 201 | } 202 | 203 | #endregion 204 | 205 | #region MatchSimpleOneWordTagAttribute 206 | 207 | internal abstract class MatchSimpleOneWordTagAttribute: MatchTagAttributeBase 208 | { 209 | #region Methods 210 | 211 | public override bool Match(string sql, int position) 212 | { 213 | return SimpleOneWordTag.MatchStart(Name, sql, position) >= 0; 214 | } 215 | 216 | #endregion 217 | 218 | #region Properties 219 | 220 | /// 221 | /// Gets the name of the tag (its identifier and sql text) 222 | /// 223 | protected abstract string Name { get; } 224 | 225 | #endregion 226 | } 227 | 228 | #endregion 229 | } 230 | -------------------------------------------------------------------------------- /TestApp/Form1.resx: -------------------------------------------------------------------------------- 1 | 2 | 3 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | text/microsoft-resx 110 | 111 | 112 | 2.0 113 | 114 | 115 | System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 116 | 117 | 118 | System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 119 | 120 | 121 | SELECT LPAD(' ',2*(LEVEL-1)) || LAST_NAME ORG_CHART, 122 | EMPLOYEE_ID, MANAGER_ID, JOB_ID 123 | FROM EMPLOYEES 124 | WHERE JOB_ID != 'FI_MGR' 125 | START WITH JOB_ID = 'AD_VP' 126 | CONNECT BY PRIOR EMPLOYEE_ID = MANAGER_ID; 127 | 128 | 129 | -------------------------------------------------------------------------------- /TestApp/Form1.designer.cs: -------------------------------------------------------------------------------- 1 | namespace TestApp 2 | { 3 | partial class Form1 4 | { 5 | /// 6 | /// Required designer variable. 7 | /// 8 | private System.ComponentModel.IContainer components = null; 9 | 10 | /// 11 | /// Clean up any resources being used. 12 | /// 13 | /// true if managed resources should be disposed; otherwise, false. 14 | protected override void Dispose(bool disposing) 15 | { 16 | if (disposing && (components != null)) 17 | { 18 | components.Dispose(); 19 | } 20 | base.Dispose(disposing); 21 | } 22 | 23 | #region Windows Form Designer generated code 24 | 25 | /// 26 | /// Required method for Designer support - do not modify 27 | /// the contents of this method with the code editor. 28 | /// 29 | private void InitializeComponent() 30 | { 31 | System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Form1)); 32 | this.fTextBoxOriginal = new System.Windows.Forms.TextBox(); 33 | this.label1 = new System.Windows.Forms.Label(); 34 | this.label2 = new System.Windows.Forms.Label(); 35 | this.fTextBoxWhereClause = new System.Windows.Forms.TextBox(); 36 | this.fTextBoxOrderBy = new System.Windows.Forms.TextBox(); 37 | this.label3 = new System.Windows.Forms.Label(); 38 | this.label4 = new System.Windows.Forms.Label(); 39 | this.fTextBoxFinal = new System.Windows.Forms.TextBox(); 40 | this.fButtonModify = new System.Windows.Forms.Button(); 41 | this.SuspendLayout(); 42 | // 43 | // fTextBoxOriginal 44 | // 45 | this.fTextBoxOriginal.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 46 | | System.Windows.Forms.AnchorStyles.Right))); 47 | this.fTextBoxOriginal.Location = new System.Drawing.Point(15, 25); 48 | this.fTextBoxOriginal.Multiline = true; 49 | this.fTextBoxOriginal.Name = "fTextBoxOriginal"; 50 | this.fTextBoxOriginal.Size = new System.Drawing.Size(617, 126); 51 | this.fTextBoxOriginal.TabIndex = 1; 52 | this.fTextBoxOriginal.Text = resources.GetString("fTextBoxOriginal.Text"); 53 | // 54 | // label1 55 | // 56 | this.label1.AutoSize = true; 57 | this.label1.Location = new System.Drawing.Point(12, 9); 58 | this.label1.Name = "label1"; 59 | this.label1.Size = new System.Drawing.Size(69, 13); 60 | this.label1.TabIndex = 0; 61 | this.label1.Text = "Original SQL:"; 62 | // 63 | // label2 64 | // 65 | this.label2.AutoSize = true; 66 | this.label2.Location = new System.Drawing.Point(12, 157); 67 | this.label2.Margin = new System.Windows.Forms.Padding(3, 3, 3, 0); 68 | this.label2.Name = "label2"; 69 | this.label2.Size = new System.Drawing.Size(126, 13); 70 | this.label2.TabIndex = 2; 71 | this.label2.Text = "Additional Where clause:"; 72 | // 73 | // fTextBoxWhereClause 74 | // 75 | this.fTextBoxWhereClause.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 76 | | System.Windows.Forms.AnchorStyles.Right))); 77 | this.fTextBoxWhereClause.Location = new System.Drawing.Point(12, 173); 78 | this.fTextBoxWhereClause.Multiline = true; 79 | this.fTextBoxWhereClause.Name = "fTextBoxWhereClause"; 80 | this.fTextBoxWhereClause.Size = new System.Drawing.Size(620, 61); 81 | this.fTextBoxWhereClause.TabIndex = 3; 82 | this.fTextBoxWhereClause.Text = "SALARY > 2500"; 83 | // 84 | // fTextBoxOrderBy 85 | // 86 | this.fTextBoxOrderBy.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Left) 87 | | System.Windows.Forms.AnchorStyles.Right))); 88 | this.fTextBoxOrderBy.Location = new System.Drawing.Point(12, 256); 89 | this.fTextBoxOrderBy.Multiline = true; 90 | this.fTextBoxOrderBy.Name = "fTextBoxOrderBy"; 91 | this.fTextBoxOrderBy.Size = new System.Drawing.Size(620, 61); 92 | this.fTextBoxOrderBy.TabIndex = 5; 93 | this.fTextBoxOrderBy.Text = "SALARY"; 94 | // 95 | // label3 96 | // 97 | this.label3.AutoSize = true; 98 | this.label3.Location = new System.Drawing.Point(12, 240); 99 | this.label3.Margin = new System.Windows.Forms.Padding(3, 3, 3, 0); 100 | this.label3.Name = "label3"; 101 | this.label3.Size = new System.Drawing.Size(137, 13); 102 | this.label3.TabIndex = 4; 103 | this.label3.Text = "Additional Order By clause:"; 104 | // 105 | // label4 106 | // 107 | this.label4.AutoSize = true; 108 | this.label4.Location = new System.Drawing.Point(12, 352); 109 | this.label4.Margin = new System.Windows.Forms.Padding(3, 3, 3, 0); 110 | this.label4.Name = "label4"; 111 | this.label4.Size = new System.Drawing.Size(55, 13); 112 | this.label4.TabIndex = 7; 113 | this.label4.Text = "Final SQL:"; 114 | // 115 | // fTextBoxFinal 116 | // 117 | this.fTextBoxFinal.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) 118 | | System.Windows.Forms.AnchorStyles.Left) 119 | | System.Windows.Forms.AnchorStyles.Right))); 120 | this.fTextBoxFinal.Location = new System.Drawing.Point(15, 368); 121 | this.fTextBoxFinal.Multiline = true; 122 | this.fTextBoxFinal.Name = "fTextBoxFinal"; 123 | this.fTextBoxFinal.Size = new System.Drawing.Size(620, 131); 124 | this.fTextBoxFinal.TabIndex = 8; 125 | // 126 | // fButtonModify 127 | // 128 | this.fButtonModify.Location = new System.Drawing.Point(12, 323); 129 | this.fButtonModify.Name = "fButtonModify"; 130 | this.fButtonModify.Size = new System.Drawing.Size(75, 23); 131 | this.fButtonModify.TabIndex = 6; 132 | this.fButtonModify.Text = "Modify SQL"; 133 | this.fButtonModify.UseVisualStyleBackColor = true; 134 | this.fButtonModify.Click += new System.EventHandler(this.fButtonModify_Click); 135 | // 136 | // Form1 137 | // 138 | this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); 139 | this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; 140 | this.ClientSize = new System.Drawing.Size(644, 511); 141 | this.Controls.Add(this.fButtonModify); 142 | this.Controls.Add(this.label4); 143 | this.Controls.Add(this.fTextBoxFinal); 144 | this.Controls.Add(this.fTextBoxOrderBy); 145 | this.Controls.Add(this.label3); 146 | this.Controls.Add(this.fTextBoxWhereClause); 147 | this.Controls.Add(this.label2); 148 | this.Controls.Add(this.label1); 149 | this.Controls.Add(this.fTextBoxOriginal); 150 | this.Font = new System.Drawing.Font("Tahoma", 8.25F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(204))); 151 | this.Name = "Form1"; 152 | this.Text = "Form1"; 153 | this.ResumeLayout(false); 154 | this.PerformLayout(); 155 | 156 | } 157 | 158 | #endregion 159 | 160 | private System.Windows.Forms.TextBox fTextBoxOriginal; 161 | private System.Windows.Forms.Label label1; 162 | private System.Windows.Forms.Label label2; 163 | private System.Windows.Forms.TextBox fTextBoxWhereClause; 164 | private System.Windows.Forms.TextBox fTextBoxOrderBy; 165 | private System.Windows.Forms.Label label3; 166 | private System.Windows.Forms.Label label4; 167 | private System.Windows.Forms.TextBox fTextBoxFinal; 168 | private System.Windows.Forms.Button fButtonModify; 169 | } 170 | } 171 | 172 | -------------------------------------------------------------------------------- /SqlParser/SqlParser.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Xml; 4 | 5 | namespace Parser 6 | { 7 | #region SqlParser 8 | 9 | public class SqlParser: ParserBase 10 | { 11 | #region Base class implementation 12 | 13 | #region Fields 14 | 15 | /// 16 | /// Stores the types of all the possible tags. 17 | /// 18 | Type[] fTags; 19 | 20 | #endregion 21 | 22 | #region Properties 23 | 24 | /// 25 | /// Indicates whether the white space is a non-valueable character. 26 | /// 27 | protected override bool IsSkipWhiteSpace 28 | { 29 | get 30 | { 31 | return true; 32 | } 33 | } 34 | 35 | /// 36 | /// Returns the list of all the available tags. 37 | /// 38 | protected override Type[] Tags 39 | { 40 | get 41 | { 42 | if (fTags == null) 43 | { 44 | fTags = new Type[] { 45 | typeof(SelectTag), 46 | typeof(FromTag), 47 | typeof(WhereTag), 48 | typeof(OrderByTag), 49 | typeof(BracesTag), 50 | typeof(StringLiteralTag), 51 | typeof(ForUpdateTag), 52 | typeof(StartWith), 53 | typeof(GroupByTag), 54 | typeof(QuotedIdentifierTag) 55 | }; 56 | } 57 | 58 | return fTags; 59 | } 60 | } 61 | 62 | #endregion 63 | 64 | #endregion 65 | 66 | #region Common 67 | 68 | #region Methods 69 | 70 | /// 71 | /// Returns the xml node which corresponds to the From tag. 72 | /// If this node does not exist, this method generates an 73 | /// exception. 74 | /// 75 | private XmlNode GetFromTagXmlNode() 76 | { 77 | XmlNode myFromNode = ParsedDocument.SelectSingleNode(string.Format(@"{0}/{1}[@{2}='{3}']", cRootXmlNodeName, cTagXmlNodeName, cTagTypeXmlAttributeName, FromTag.cTagName)); 78 | if (myFromNode == null) 79 | throw new Exception(ToText()); 80 | 81 | return myFromNode; 82 | } 83 | 84 | /// 85 | /// Returns the xml node which corresponds to the For Update tag. 86 | /// 87 | private XmlNode GetForUpdateTagXmlNode() 88 | { 89 | XmlNode myForUpdateNode = ParsedDocument.SelectSingleNode(string.Format(@"{0}/{1}[@{2}='{3}']", cRootXmlNodeName, cTagXmlNodeName, cTagTypeXmlAttributeName, ForUpdateTag.cTagName)); 90 | 91 | return myForUpdateNode; 92 | } 93 | 94 | /// 95 | /// Checks whether there is a tag in the text at the specified position, and returns its tag. 96 | /// 97 | internal new Type IsTag(string text, int position) 98 | { 99 | return base.IsTag(text, position); 100 | } 101 | 102 | #endregion 103 | 104 | #endregion 105 | 106 | #region Where Clause 107 | 108 | #region Methods 109 | 110 | /// 111 | /// Returns the xml node which corresponds to the Where tag. 112 | /// If this node does not exist, creates a new one (if needed). 113 | /// 114 | private XmlNode GetWhereTagXmlNode(bool createNew) 115 | { 116 | XmlNode myWhereNode = ParsedDocument.SelectSingleNode(string.Format(@"{0}/{1}[@{2}='{3}']", cRootXmlNodeName, cTagXmlNodeName, cTagTypeXmlAttributeName, WhereTag.cTagName)); 117 | if (myWhereNode == null && createNew) 118 | { 119 | WhereTag myWhereTag = new WhereTag(); 120 | myWhereTag.InitializeFromData(this, null, false); 121 | myWhereNode = CreateTagXmlNode(myWhereTag); 122 | 123 | XmlNode myFromNode = GetFromTagXmlNode(); 124 | myFromNode.ParentNode.InsertAfter(myWhereNode, myFromNode); 125 | } 126 | 127 | return myWhereNode; 128 | } 129 | 130 | #endregion 131 | 132 | #region Properties 133 | 134 | /// 135 | /// Gets or sets the Where clause of the parsed sql. 136 | /// 137 | public string WhereClause 138 | { 139 | get 140 | { 141 | #region Get the Where xml node 142 | 143 | XmlNode myWhereTagXmlNode = GetWhereTagXmlNode(false); 144 | if (myWhereTagXmlNode == null) 145 | return string.Empty; 146 | 147 | #endregion 148 | 149 | StringBuilder myStringBuilder = new StringBuilder(); 150 | XmlNodesToText(myStringBuilder, myWhereTagXmlNode.ChildNodes); 151 | return myStringBuilder.ToString(); 152 | } 153 | set 154 | { 155 | if (string.IsNullOrEmpty(value)) 156 | { 157 | #region Remove the Where xml node 158 | 159 | XmlNode myWhereXmlNode = GetWhereTagXmlNode(false); 160 | if (myWhereXmlNode != null) 161 | myWhereXmlNode.ParentNode.RemoveChild(myWhereXmlNode); 162 | 163 | #endregion 164 | } 165 | else 166 | { 167 | #region Modify the Where xml node 168 | 169 | XmlNode myWhereXmlNode = GetWhereTagXmlNode(true); 170 | 171 | ClearXmlNode(myWhereXmlNode); 172 | 173 | TagBase myWhereTag = TagXmlNodeToTag(myWhereXmlNode); 174 | 175 | ParseBlock(myWhereXmlNode, myWhereTag, value, 0); 176 | 177 | #endregion 178 | } 179 | } 180 | } 181 | 182 | #endregion 183 | 184 | #endregion 185 | 186 | #region Order By Clause 187 | 188 | #region Methods 189 | 190 | /// 191 | /// Returns the xml node which corresponds to the Order By tag. 192 | /// If this node does not exist, creates a new one (if needed). 193 | /// 194 | private XmlNode GetOrderByTagXmlNode(bool createNew) 195 | { 196 | XmlNode myOrderByNode = ParsedDocument.SelectSingleNode(string.Format(@"{0}/{1}[@{2}='{3}']", cRootXmlNodeName, cTagXmlNodeName, cTagTypeXmlAttributeName, OrderByTag.cTagName)); 197 | if (myOrderByNode == null && createNew) 198 | { 199 | OrderByTag myOrderByTag = new OrderByTag(); 200 | myOrderByTag.InitializeFromData(this, null, false); 201 | myOrderByNode = CreateTagXmlNode(myOrderByTag); 202 | 203 | XmlNode myForUpdateNode = GetForUpdateTagXmlNode(); 204 | if (myForUpdateNode != null) 205 | { 206 | myForUpdateNode.ParentNode.InsertBefore(myOrderByNode, myForUpdateNode); 207 | return myOrderByNode; 208 | } 209 | 210 | XmlNode myFromNode = GetFromTagXmlNode(); 211 | myFromNode.ParentNode.AppendChild(myOrderByNode); 212 | } 213 | 214 | return myOrderByNode; 215 | } 216 | 217 | #endregion 218 | 219 | #region Properties 220 | 221 | /// 222 | /// Gets or sets the Order By clause of the parsed sql. 223 | /// 224 | public string OrderByClause 225 | { 226 | get 227 | { 228 | #region Get the Order By xml node 229 | 230 | XmlNode myOrderByTagXmlNode = GetOrderByTagXmlNode(false); 231 | if (myOrderByTagXmlNode == null) 232 | return string.Empty; 233 | 234 | #endregion 235 | 236 | StringBuilder myStringBuilder = new StringBuilder(); 237 | XmlNodesToText(myStringBuilder, myOrderByTagXmlNode.ChildNodes); 238 | return myStringBuilder.ToString(); 239 | } 240 | set 241 | { 242 | if (string.IsNullOrEmpty(value)) 243 | { 244 | #region Remove the Order By xml node 245 | 246 | XmlNode myOrderByXmlNode = GetOrderByTagXmlNode(false); 247 | if (myOrderByXmlNode != null) 248 | myOrderByXmlNode.ParentNode.RemoveChild(myOrderByXmlNode); 249 | 250 | #endregion 251 | } 252 | else 253 | { 254 | #region Modify the Order By xml node 255 | 256 | XmlNode myOrderByXmlNode = GetOrderByTagXmlNode(true); 257 | 258 | ClearXmlNode(myOrderByXmlNode); 259 | 260 | TagBase myOrderByTag = TagXmlNodeToTag(myOrderByXmlNode); 261 | 262 | ParseBlock(myOrderByXmlNode, myOrderByTag, value, 0); 263 | 264 | #endregion 265 | } 266 | } 267 | } 268 | 269 | #endregion 270 | 271 | #endregion 272 | } 273 | 274 | #endregion 275 | } 276 | -------------------------------------------------------------------------------- /SqlParser/ClassDiagram.cd: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | ForUpdateTag.cs 8 | AAAAAAAAAAAAAAEAAIAAgAQAAgAAAAAAAAAAAABAAAA= 9 | 10 | 11 | 12 | 13 | 14 | ForUpdateTag.cs 15 | AAAAAAAAAAAAAAAAAIAAAAQAAgAAAAAAAAAAAAAAAAA= 16 | 17 | 18 | 19 | 20 | 21 | FromTag.cs 22 | AAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAABAAAA= 23 | 24 | 25 | 26 | 27 | 28 | FromTag.cs 29 | AAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAA= 30 | 31 | 32 | 33 | 34 | 35 | GroupByTag.cs 36 | AAAAAAAAAAAAAAEAAIAAgAQAAgAAAAAAAAAAAABAAAA= 37 | 38 | 39 | 40 | 41 | 42 | GroupByTag.cs 43 | AAAAAAAAAAAAAAAAAIAAAAQAAgAAAAAAAAAAAAAAAAA= 44 | 45 | 46 | 47 | 48 | 49 | OrderByTag.cs 50 | AAAAAAAAAAAAAAEAAIAAgAQAAgAAAAAAAAAAAABAAAA= 51 | 52 | 53 | 54 | 55 | 56 | OrderByTag.cs 57 | AAAAAAAAAAAAAAAAAIAAAAQAAgAAAAAAAAAAAAAAAAA= 58 | 59 | 60 | 61 | 62 | 63 | QuotedIdentifierTag.cs 64 | AAQAACAAEAAAAAAAICAAAAAAAAAAAAAAAAAAAABAAAA= 65 | 66 | 67 | 68 | 69 | 70 | QuotedIdentifierTag.cs 71 | AAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= 72 | 73 | 74 | 75 | 76 | 77 | SelectTag.cs 78 | AAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAABAAAA= 79 | 80 | 81 | 82 | 83 | 84 | SelectTag.cs 85 | AAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAA= 86 | 87 | 88 | 89 | 90 | 91 | SimpleOneWordTag.cs 92 | IAQQACAAEAAAAAAAACAAAAQEAAAAAAAAABAAAAAAAAA= 93 | 94 | 95 | 96 | 97 | 98 | SimpleOneWordTag.cs 99 | AAAAAgAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAA= 100 | 101 | 102 | 103 | 104 | 105 | SimpleTwoWordTag.cs 106 | AAAQACAAAAAAAAAAAIAAAAAAAgAAAAAAAAAAAAAAAAA= 107 | 108 | 109 | 110 | 111 | 112 | SimpleTwoWordTag.cs 113 | AAAAAgAAAAAAAAAAAIAAAAAAAgAAAAAAAAAAAAAAAAA= 114 | 115 | 116 | 117 | 118 | 119 | StartWith.cs 120 | AAAAAAAAAAAAAAEAAIAAgAQAAgAAAAAAAAAAAABAAAA= 121 | 122 | 123 | 124 | 125 | 126 | StartWith.cs 127 | AAAAAAAAAAAAAAAAAIAAAAQAAgAAAAAAAAAAAAAAAAA= 128 | 129 | 130 | 131 | 132 | 133 | StringLiteralTag.cs 134 | AAQAACAAEAAAAAAAICAAAAAAAAAAAAAAAAAAAABAAAA= 135 | 136 | 137 | 138 | 139 | 140 | StringLiteralTag.cs 141 | AAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= 142 | 143 | 144 | 145 | 146 | 147 | TagBase.cs 148 | AAQgACAAEAAAIEAAASAAAAIACAACAAAAAxEgBAAAICA= 149 | 150 | 151 | 152 | 153 | 154 | TagBase.cs 155 | AAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= 156 | 157 | 158 | 159 | 160 | 161 | WhereTag.cs 162 | AAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAABAAAA= 163 | 164 | 165 | 166 | 167 | 168 | WhereTag.cs 169 | AAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAA= 170 | 171 | 172 | 173 | 174 | 175 | BracesTag.cs 176 | AAQAACAQEAAAAgAAICAAAAAAAAAAAAAAAABAAABAAAA= 177 | 178 | 179 | 180 | 181 | 182 | BracesTag.cs 183 | AAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= 184 | 185 | 186 | -------------------------------------------------------------------------------- /SqlParser/TagBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Xml; 3 | using System.Text; 4 | 5 | namespace Parser 6 | { 7 | #region TagBase 8 | 9 | /// 10 | /// The base class for all the tags. 11 | /// 12 | public abstract class TagBase 13 | { 14 | #region Fields 15 | 16 | /// 17 | /// The value of the IsInitialized property. 18 | /// 19 | private bool fIsInitialized = false; 20 | 21 | /// 22 | /// The value of the HasContents property. 23 | /// 24 | private bool fHasContents = false; 25 | 26 | /// 27 | /// The value of the Value property. 28 | /// 29 | private string fValue; 30 | 31 | /// 32 | /// The value of the Parser property. 33 | /// 34 | private ParserBase fParser; 35 | 36 | #endregion 37 | 38 | #region Methods 39 | 40 | #region Initialization 41 | 42 | /// 43 | /// Reads the tag at the specified position in the specified word and separator array. 44 | /// 45 | /// 46 | /// The parent tag of this tag. This argument is used to determine 47 | /// the end of the tag (it can be the end of the parent tag). 48 | /// 49 | /// 50 | /// The position after the tag (at which to continue reading). 51 | /// 52 | public int InitializeFromText(ParserBase parser, string text, int position, TagBase parentTag) 53 | { 54 | #region Check the arguments 55 | 56 | if (parser == null) 57 | throw new ArgumentNullException("parser"); 58 | 59 | #endregion 60 | 61 | int myResult = InitializeCoreFromText(parser, text, position, parentTag); 62 | 63 | IsInitialized = true; 64 | 65 | return myResult; 66 | } 67 | 68 | /// 69 | /// Reads the tag at the specified position in the specified word and separator array. 70 | /// 71 | /// 72 | /// The parent tag of this tag. This argument is used to determine 73 | /// the end of the tag (it can be the end of the parent tag). 74 | /// 75 | /// 76 | /// The position after the tag (at which to continue reading). 77 | /// 78 | protected abstract int InitializeCoreFromText(ParserBase parser, string text, int position, TagBase parentTag); 79 | 80 | /// 81 | /// Reads the tag from the specified data. 82 | /// 83 | public void InitializeFromData(ParserBase parser, string value, bool hasContents) 84 | { 85 | #region Check the arguments 86 | 87 | if (parser == null) 88 | throw new ArgumentNullException("parser"); 89 | 90 | #endregion 91 | 92 | InitializeFromDataCore(parser, value, hasContents); 93 | 94 | IsInitialized = true; 95 | } 96 | 97 | /// 98 | /// Reads the tag from the specified data. 99 | /// 100 | protected virtual void InitializeFromDataCore(ParserBase parser, string value, bool hasContents) 101 | { 102 | Parser = parser; 103 | 104 | Value = value; 105 | 106 | HasContents = hasContents; 107 | 108 | IsInitialized = true; 109 | } 110 | 111 | #endregion 112 | 113 | #region Attributes-Related (Static) 114 | 115 | /// 116 | /// Returns the type of the specified tag (its identifier). 117 | /// This method is used by the Type property. 118 | /// 119 | public static string GetTagType(Type tag) 120 | { 121 | #region Check the arguments 122 | 123 | if (tag == null) 124 | throw new ArgumentNullException("tag"); 125 | 126 | #endregion 127 | 128 | object[] myTagTypeAttributes = tag.GetCustomAttributes(typeof(TagTypeAttribute), true); 129 | 130 | if (myTagTypeAttributes == null || myTagTypeAttributes.Length == 0) 131 | throw new Exception("Cannot find a tag type attribute."); 132 | 133 | if (myTagTypeAttributes.Length > 1) 134 | throw new Exception("Ambiguous tag type."); 135 | 136 | return (myTagTypeAttributes[0] as TagTypeAttribute).Type; 137 | } 138 | 139 | /// 140 | /// Returns the match attribute of the specified class. 141 | /// This attribute is used to identify a tag in a text. 142 | /// 143 | public static MatchTagAttributeBase GetTagMatchAttribute(Type tag) 144 | { 145 | #region Check the arguments 146 | 147 | if (tag == null) 148 | throw new ArgumentNullException("tag"); 149 | 150 | #endregion 151 | 152 | object[] myMatchTagAttributes = tag.GetCustomAttributes(typeof(MatchTagAttributeBase), true); 153 | 154 | if (myMatchTagAttributes == null || myMatchTagAttributes.Length == 0) 155 | throw new Exception("Cannot find a match tag attribute."); 156 | 157 | if (myMatchTagAttributes.Length > 1) 158 | throw new Exception("Ambiguous match tag."); 159 | 160 | return myMatchTagAttributes[0] as MatchTagAttributeBase; 161 | } 162 | 163 | #endregion 164 | 165 | #region Common 166 | 167 | /// 168 | /// Returns a value indicating whether there is the tag ending 169 | /// at the specified position. 170 | /// 171 | /// 172 | /// If this value is less than zero, then there is no ending; otherwise the 173 | /// position after ending is returned. 174 | /// 175 | public virtual int MatchEnd(string text, int position) 176 | { 177 | CheckInitialized(); 178 | 179 | throw new System.NotImplementedException(); 180 | } 181 | 182 | /// 183 | /// Writes the start of the tag. 184 | /// 185 | public abstract void WriteStart(StringBuilder output); 186 | 187 | /// 188 | /// Writes the end of the tag. 189 | /// 190 | public virtual void WriteEnd(StringBuilder output) 191 | { 192 | CheckInitialized(); 193 | } 194 | 195 | /// 196 | /// Checks whether the tag is initialized, and throws an exception if not. This method should be called before a tag porperty or method is accessed. 197 | /// 198 | protected void CheckInitialized() 199 | { 200 | if (!IsInitialized) 201 | throw new Exception("Tag is not initialized."); 202 | } 203 | 204 | #endregion 205 | 206 | #endregion 207 | 208 | #region Properties 209 | 210 | /// 211 | /// Returns the type of the tag (its identifier). 212 | /// 213 | public string Type 214 | { 215 | get 216 | { 217 | return GetTagType(GetType()); 218 | } 219 | } 220 | 221 | /// 222 | /// The value of the tag (it can be the text of a string literal for example). 223 | /// 224 | public string Value 225 | { 226 | get 227 | { 228 | CheckInitialized(); 229 | 230 | return fValue; 231 | } 232 | protected set 233 | { 234 | fValue = value; 235 | } 236 | } 237 | 238 | /// 239 | /// Gets a value indicating whether this tag has contents to be parsed. 240 | /// 241 | public virtual bool HasContents 242 | { 243 | get 244 | { 245 | CheckInitialized(); 246 | 247 | return fHasContents; 248 | } 249 | protected set 250 | { 251 | fHasContents = value; 252 | } 253 | } 254 | 255 | /// 256 | /// Inidicates whether the string end can be treated as the end of the tag. 257 | /// 258 | public virtual bool CanTerminateByStringEnd 259 | { 260 | get 261 | { 262 | CheckInitialized(); 263 | 264 | return false; 265 | } 266 | } 267 | 268 | /// 269 | /// Indicates whether the tag has been initialized. 270 | /// 271 | protected bool IsInitialized 272 | { 273 | get 274 | { 275 | return fIsInitialized; 276 | } 277 | private set 278 | { 279 | fIsInitialized = value; 280 | } 281 | } 282 | 283 | /// 284 | /// The parser which has created this instance. 285 | /// 286 | public ParserBase Parser 287 | { 288 | get 289 | { 290 | CheckInitialized(); 291 | 292 | return fParser; 293 | } 294 | protected set 295 | { 296 | fParser = value; 297 | } 298 | } 299 | 300 | #endregion 301 | } 302 | 303 | #endregion 304 | 305 | #region MatchTagAttributeBase 306 | 307 | /// 308 | /// This attribute is used to determine whether there is certain tag 309 | /// at a position in a text. 310 | /// 311 | public abstract class MatchTagAttributeBase : Attribute 312 | { 313 | public abstract bool Match(string text, int position); 314 | } 315 | 316 | #endregion 317 | 318 | #region TagTypeAttribute 319 | 320 | /// 321 | /// This attribute is used to determine the name (identifier) of a tag. 322 | /// 323 | public sealed class TagTypeAttribute : Attribute 324 | { 325 | #region Fields 326 | 327 | private string fType; 328 | 329 | #endregion 330 | 331 | #region Methods 332 | 333 | public TagTypeAttribute(string type) 334 | { 335 | Type = type; 336 | } 337 | 338 | #endregion 339 | 340 | #region Properties 341 | 342 | public string Type 343 | { 344 | get 345 | { 346 | return fType; 347 | } 348 | private set 349 | { 350 | fType = value; 351 | } 352 | } 353 | 354 | #endregion 355 | } 356 | 357 | #endregion 358 | } -------------------------------------------------------------------------------- /SqlParser/ParserBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Xml; 4 | 5 | namespace Parser 6 | { 7 | #region ParserBase 8 | 9 | /// 10 | /// An abstract class which is used as the base class for parsers. 11 | /// It contains basic methods which allow you to create a logical tree 12 | /// from a document. 13 | /// 14 | public abstract class ParserBase 15 | { 16 | #region Consts 17 | 18 | /// 19 | /// The name of the Value xml attribute. 20 | /// 21 | protected const string cValueXmlAttributeName = "Value"; 22 | 23 | /// 24 | /// The name of the Tag Type xml attribute. 25 | /// 26 | protected const string cTagTypeXmlAttributeName = "Type"; 27 | 28 | /// 29 | /// The name of the root node in the xml representation of a parsed text. 30 | /// 31 | protected const string cRootXmlNodeName = "ParsedDocument"; 32 | 33 | /// 34 | /// The name of the Tag xml node. 35 | /// 36 | protected const string cTagXmlNodeName = "Tag"; 37 | 38 | /// 39 | /// The name of the Text xml node. 40 | /// 41 | protected const string cTextXmlNodeName = "Text"; 42 | 43 | /// 44 | /// The white space text (is used when adding white 45 | /// spaces between text elements). 46 | /// 47 | public const string cWhiteSpace = " "; 48 | 49 | /// 50 | /// The new line string. 51 | /// 52 | public const string cNewLine = "\r\n"; 53 | 54 | #endregion 55 | 56 | #region Fields 57 | 58 | /// 59 | /// The tree representation of a parsed document as an xml document. 60 | /// 61 | private XmlDocument fParsedDocument; 62 | 63 | #endregion 64 | 65 | #region Methods 66 | 67 | /// 68 | /// Parses the specified text. 69 | /// 70 | public void Parse(string text) 71 | { 72 | #region Check the arguments 73 | 74 | if (text == null) 75 | throw new ArgumentNullException("text"); 76 | 77 | #endregion 78 | 79 | fParsedDocument = new XmlDocument(); 80 | XmlNode myRootNode = fParsedDocument.CreateElement(cRootXmlNodeName); 81 | fParsedDocument.AppendChild(myRootNode); 82 | 83 | try 84 | { 85 | ParseBlock(myRootNode, null, text, 0); 86 | } 87 | catch 88 | { 89 | fParsedDocument = null; 90 | throw; 91 | } 92 | 93 | // m_ParsedDocument.Save(@"D:\Temp\ParserTest.xml"); 94 | } 95 | 96 | /// 97 | /// Parses the specified block of a text. 98 | /// 99 | /// 100 | /// Returns the end position of the parsed block. 101 | /// 102 | protected int ParseBlock(XmlNode parentNode, TagBase parentTag, string text, int position) 103 | { 104 | #region Check the arguments 105 | 106 | if (parentNode == null) 107 | throw new ArgumentNullException("parentNode"); 108 | 109 | CheckTextAndPositionArguments(text, position); 110 | 111 | #endregion 112 | 113 | while (position < text.Length) 114 | { 115 | if (IsSkipWhiteSpace) 116 | SkipWhiteSpace(text, ref position); 117 | 118 | if (position == text.Length) 119 | break; 120 | 121 | #region Read the parent tag ending 122 | 123 | if (parentTag != null) 124 | { 125 | int myParentTagEndingEndPosition = parentTag.MatchEnd(text, position); 126 | if (myParentTagEndingEndPosition >= 0) 127 | { 128 | position = myParentTagEndingEndPosition; 129 | return position; 130 | } 131 | } 132 | 133 | #endregion 134 | 135 | Type myTagType = IsTag(text, position); 136 | if (myTagType != null) 137 | { 138 | #region Read a tag 139 | 140 | #region Create the tag class instance 141 | 142 | TagBase myTag = Activator.CreateInstance(myTagType) as TagBase; 143 | position = myTag.InitializeFromText(this, text, position, parentTag); 144 | 145 | #endregion 146 | 147 | #region Create an xml node for the tag 148 | 149 | XmlNode myTagXmlNode = CreateTagXmlNode(myTag); 150 | parentNode.AppendChild(myTagXmlNode); 151 | 152 | #endregion 153 | 154 | if (myTag.HasContents) 155 | position = ParseBlock(myTagXmlNode, myTag, text, position); 156 | 157 | #endregion 158 | } 159 | else 160 | { 161 | #region Read text 162 | 163 | string myText = ReadWordOrSeparator(text, ref position, !IsSkipWhiteSpace); 164 | parentNode.AppendChild(CreateTextXmlNode(myText)); 165 | 166 | #endregion 167 | } 168 | } 169 | 170 | if (parentTag != null && !parentTag.CanTerminateByStringEnd) 171 | throw new Exception("Invalid format"); 172 | 173 | return position; 174 | } 175 | 176 | /// 177 | /// Checks whether there is a tag in the text at the specified position, and returns its type. 178 | /// 179 | protected Type IsTag(string text, int position) 180 | { 181 | foreach (Type myTagType in Tags) 182 | { 183 | MatchTagAttributeBase myMatchTagAttribute = TagBase.GetTagMatchAttribute(myTagType); 184 | if (myMatchTagAttribute.Match(text, position)) 185 | return myTagType; 186 | } 187 | 188 | return null; 189 | } 190 | 191 | /// 192 | /// Creates an xml node for the specified tag. 193 | /// 194 | protected XmlNode CreateTagXmlNode(TagBase tag) 195 | { 196 | #region Check the arguments 197 | 198 | if (tag == null) 199 | throw new ArgumentNullException(); 200 | 201 | #endregion 202 | 203 | CheckXmlDocInitialized(); 204 | 205 | XmlElement myTagNode = fParsedDocument.CreateElement(cTagXmlNodeName); 206 | 207 | XmlAttribute myTypeAttribute = fParsedDocument.CreateAttribute(cTagTypeXmlAttributeName); 208 | myTypeAttribute.Value = tag.Type; 209 | myTagNode.Attributes.Append(myTypeAttribute); 210 | 211 | if (tag.Value != null) 212 | { 213 | XmlAttribute myValueAttribute = fParsedDocument.CreateAttribute(cValueXmlAttributeName); 214 | myValueAttribute.Value = tag.Value; 215 | myTagNode.Attributes.Append(myValueAttribute); 216 | } 217 | 218 | return myTagNode; 219 | } 220 | 221 | /// 222 | /// Creates an xml node for the specified text. 223 | /// 224 | protected XmlNode CreateTextXmlNode(string text) 225 | { 226 | #region Check the arguments 227 | 228 | if (text == null) 229 | throw new ArgumentNullException("text"); 230 | 231 | #endregion 232 | 233 | CheckXmlDocInitialized(); 234 | 235 | XmlElement myTextNode = fParsedDocument.CreateElement(cTextXmlNodeName); 236 | 237 | XmlAttribute myValueAttribute = fParsedDocument.CreateAttribute(cValueXmlAttributeName); 238 | myValueAttribute.Value = text; 239 | myTextNode.Attributes.Append(myValueAttribute); 240 | 241 | return myTextNode; 242 | } 243 | 244 | /// 245 | /// Skips the white space symbols located at the specified position. 246 | /// 247 | public static void SkipWhiteSpace(string text, ref int position) 248 | { 249 | #region Check the parameters 250 | 251 | CheckTextAndPositionArguments(text, position); 252 | 253 | #endregion 254 | 255 | while (position < text.Length) 256 | { 257 | if (!char.IsWhiteSpace(text, position)) 258 | break; 259 | position++; 260 | } 261 | } 262 | 263 | /// 264 | /// Reads a single word or separator at the specified position. 265 | /// 266 | public static string ReadWordOrSeparator(string text, ref int position, bool treatWhiteSpaceAsSeparator) 267 | { 268 | #region Check the parameters 269 | 270 | CheckTextAndPositionArguments(text, position); 271 | 272 | #endregion 273 | 274 | int myStartPosition = position; 275 | 276 | while (position < text.Length) 277 | { 278 | #region Check is white space 279 | 280 | if (char.IsWhiteSpace(text, position)) 281 | { 282 | if (position == myStartPosition && treatWhiteSpaceAsSeparator) 283 | { 284 | if (position + cNewLine.Length <= text.Length && text.Substring(position, cNewLine.Length) == cNewLine) 285 | position += cNewLine.Length; 286 | else 287 | position += 1; 288 | } 289 | break; 290 | } 291 | 292 | #endregion 293 | 294 | #region Check is separator 295 | 296 | if (!char.IsLetterOrDigit(text, position) && text[position] != '_') 297 | { 298 | if (position == myStartPosition) 299 | position++; 300 | break; 301 | } 302 | 303 | #endregion 304 | 305 | position++; 306 | } 307 | 308 | if (position == myStartPosition) 309 | return string.Empty; 310 | 311 | return text.Substring(myStartPosition, position - myStartPosition); 312 | } 313 | 314 | /// 315 | /// Checks the text and position parameters. 316 | /// 317 | public static void CheckTextAndPositionArguments(string text, int position) 318 | { 319 | #region Check the parameters 320 | 321 | if (text == null) 322 | throw new ArgumentNullException("text"); 323 | 324 | if (position < 0 || position >= text.Length) 325 | throw new ArgumentOutOfRangeException("position"); 326 | 327 | #endregion 328 | } 329 | 330 | /// 331 | /// Checks whether the m_ParsedDocument is initialized. 332 | /// 333 | protected void CheckXmlDocInitialized() 334 | { 335 | if (fParsedDocument == null) 336 | throw new Exception("Xml document is not initialized"); 337 | } 338 | 339 | /// 340 | /// Returns the text string processed by the parser. 341 | /// 342 | public string ToText() 343 | { 344 | CheckXmlDocInitialized(); 345 | 346 | StringBuilder myStringBuilder = new StringBuilder(); 347 | 348 | XmlNodesToText(myStringBuilder, fParsedDocument.ChildNodes[0].ChildNodes); 349 | 350 | return myStringBuilder.ToString(); 351 | } 352 | 353 | /// 354 | /// Converts the specified xml node collection to the text. 355 | /// 356 | protected void XmlNodesToText(StringBuilder output, XmlNodeList nodes) 357 | { 358 | #region Check arguments 359 | 360 | if (nodes == null) 361 | throw new ArgumentNullException("nodes"); 362 | 363 | if (output == null) 364 | throw new ArgumentNullException("output"); 365 | 366 | #endregion 367 | 368 | foreach (XmlNode myNode in nodes) 369 | XmlNodeToText(output, myNode); 370 | } 371 | 372 | /// 373 | /// Converts the specified xml node to text. 374 | /// 375 | protected void XmlNodeToText(StringBuilder output, XmlNode node) 376 | { 377 | #region Check arguments 378 | 379 | if (node == null) 380 | throw new ArgumentNullException("node"); 381 | 382 | if (output == null) 383 | throw new ArgumentNullException("output"); 384 | 385 | #endregion 386 | 387 | if (IsTagXmlNode(node)) 388 | { 389 | TagXmlNodeToText(output, node); 390 | } 391 | else if (IsTextXmlNode(node)) 392 | { 393 | output.Append(GetTextNodeText(node)); 394 | 395 | if (IsSkipWhiteSpace) 396 | output.Append(cWhiteSpace); 397 | } 398 | else 399 | { 400 | throw new Exception("Unrecognized xml node."); 401 | } 402 | 403 | //m_ParsedDocument.Save(@"D:\Temp\testparser.xml"); 404 | } 405 | 406 | /// 407 | /// Determines whether the specified node is a tag node. 408 | /// 409 | protected bool IsTagXmlNode(XmlNode node) 410 | { 411 | #region Check arguments 412 | 413 | if (node == null) 414 | throw new ArgumentNullException("node"); 415 | 416 | #endregion 417 | 418 | return node.Name == cTagXmlNodeName; 419 | } 420 | 421 | /// 422 | /// Determines whether the specified node is a text node. 423 | /// 424 | protected bool IsTextXmlNode(XmlNode node) 425 | { 426 | #region Check arguments 427 | 428 | if (node == null) 429 | throw new ArgumentNullException("node"); 430 | 431 | #endregion 432 | 433 | return node.Name == cTextXmlNodeName; 434 | } 435 | 436 | /// 437 | /// Returns the text stored in the text node. 438 | /// 439 | protected string GetTextNodeText(XmlNode node) 440 | { 441 | string myValue = GetXmlNodeValue(node); 442 | if (myValue == null) 443 | throw new Exception("Cannot get text node's value."); 444 | 445 | return myValue; 446 | } 447 | 448 | /// 449 | /// Retrieves the value of the specified tag xml node. 450 | /// 451 | protected string GetXmlNodeValue(XmlNode node) 452 | { 453 | #region Check arguments 454 | 455 | if (node == null) 456 | throw new ArgumentNullException("node"); 457 | 458 | #endregion 459 | 460 | XmlAttribute myValueXmlAttribute = node.Attributes[cValueXmlAttributeName]; 461 | if (myValueXmlAttribute == null) 462 | return null; 463 | 464 | return myValueXmlAttribute.Value; 465 | } 466 | 467 | /// 468 | /// Converts the specified tag xml node to text. 469 | /// 470 | private void TagXmlNodeToText(StringBuilder output, XmlNode node) 471 | { 472 | #region Check arguments 473 | 474 | if (node == null) 475 | throw new ArgumentNullException("node"); 476 | 477 | if (output == null) 478 | throw new ArgumentNullException("output"); 479 | 480 | #endregion 481 | 482 | TagBase myTag = TagXmlNodeToTag(node); 483 | 484 | myTag.WriteStart(output); 485 | 486 | if (IsSkipWhiteSpace) 487 | output.Append(cWhiteSpace); 488 | 489 | if (node.ChildNodes.Count > 0) 490 | XmlNodesToText(output, node.ChildNodes); 491 | 492 | myTag.WriteEnd(output); 493 | 494 | if (IsSkipWhiteSpace) 495 | output.Append(cWhiteSpace); 496 | } 497 | 498 | /// 499 | /// Converts the specified tag xml node into a tag. 500 | /// 501 | protected TagBase TagXmlNodeToTag(XmlNode node) 502 | { 503 | #region Check arguments 504 | 505 | if (node == null) 506 | throw new ArgumentNullException("node"); 507 | 508 | #endregion 509 | 510 | #region Get tag parameters 511 | 512 | XmlAttribute myTagTypeXmlAttribute = node.Attributes[cTagTypeXmlAttributeName]; 513 | if (myTagTypeXmlAttribute == null) 514 | throw new Exception("Cannot find the tag type attribute"); 515 | string myTagType = myTagTypeXmlAttribute.Value; 516 | 517 | XmlAttribute myValueXmlAttribute = node.Attributes[cValueXmlAttributeName]; 518 | string myTagValue; 519 | if (myValueXmlAttribute == null) 520 | myTagValue = null; 521 | else 522 | myTagValue = myValueXmlAttribute.Value; 523 | 524 | #endregion 525 | 526 | return GetTagFromType(myTagType, myTagValue, node.ChildNodes.Count > 0); 527 | } 528 | 529 | /// 530 | /// Retrurns the tag instance from its type and value. 531 | /// 532 | protected TagBase GetTagFromType(string type, string value, bool hasContents) 533 | { 534 | foreach (Type myTagType in Tags) 535 | { 536 | string myType = TagBase.GetTagType(myTagType); 537 | if (string.Compare(myType, type, true) == 0) 538 | { 539 | TagBase myTag = Activator.CreateInstance(myTagType) as TagBase; 540 | myTag.InitializeFromData(this, value, hasContents); 541 | return myTag; 542 | } 543 | } 544 | 545 | throw new Exception(string.Format("Tag cannot be found: {0}", type)); 546 | } 547 | 548 | /// 549 | /// Removes all the sub nodes from the specified node. 550 | /// 551 | protected static void ClearXmlNode(XmlNode node) 552 | { 553 | #region Check arguments 554 | 555 | if (node == null) 556 | throw new ArgumentNullException("node"); 557 | 558 | #endregion 559 | 560 | for (int myChildIndex = node.ChildNodes.Count - 1; myChildIndex >= 0; myChildIndex--) 561 | node.RemoveChild(node.ChildNodes[myChildIndex]); 562 | } 563 | 564 | /// 565 | /// Returns how many continuous specified strings are before of the specified 566 | /// position in the specified text 567 | /// 568 | public static int CountStringsBefore(string text, int position, string @string) 569 | { 570 | CheckTextAndPositionArguments(text, position); 571 | 572 | #region Count the Strings before 573 | 574 | int myStringsBeforeCount = 0; 575 | int myPrevPosition = position - @string.Length; 576 | while (myPrevPosition >= 0) 577 | { 578 | // If it is not the specified string prefix 579 | if (string.Compare(text, myPrevPosition, @string, 0, @string.Length, true) != 0) 580 | break; 581 | 582 | myStringsBeforeCount++; 583 | myPrevPosition -= @string.Length; 584 | } 585 | 586 | #endregion 587 | 588 | return myStringsBeforeCount; 589 | } 590 | 591 | #endregion 592 | 593 | #region Properties 594 | 595 | /// 596 | /// Indicates whether the white space is considered as a non-valuable 597 | /// character. 598 | /// 599 | protected abstract bool IsSkipWhiteSpace { get; } 600 | 601 | /// 602 | /// The list of all available tags. 603 | /// 604 | protected abstract Type[] Tags { get; } 605 | 606 | /// 607 | /// Gets the parsed document in xml format. 608 | /// 609 | protected XmlDocument ParsedDocument 610 | { 611 | get 612 | { 613 | CheckXmlDocInitialized(); 614 | 615 | return fParsedDocument; 616 | } 617 | } 618 | 619 | #endregion 620 | } 621 | 622 | #endregion 623 | } 624 | --------------------------------------------------------------------------------