2002-04-12 Duncan Mak <duncan@ximian.com>
[mono.git] / mcs / class / System.XML / System.Xml.XPath / XPathScanner.cs
1 //\r
2 // System.Xml.XPath.XPathScanner\r
3 //\r
4 // Author:\r
5 //   Jason Diamond (jason@injektilo.org)\r
6 //\r
7 // (C) 2002 Jason Diamond  http://injektilo.org/\r
8 //\r
9 \r
10 using System;\r
11 using System.IO;\r
12 using System.Text;\r
13 \r
14 // [28] ExprToken         ::= '(' | ')' | '[' | ']' | '.' | '..' | '@' | ',' | '::'\r
15 //                            | NameTest\r
16 //                            | NodeType\r
17 //                            | Operator\r
18 //                            | FunctionName\r
19 //                            | AxisName\r
20 //                            | Literal\r
21 //                            | Number\r
22 //                            | VariableReference\r
23 // [29] Literal           ::= '"' [^"]* '"'\r
24 //                            | "'" [^']* "'"\r
25 // [30] Number            ::= Digits ('.' Digits?)?\r
26 //                            | '.' Digits\r
27 // [31] Digits            ::= [0-9]+\r
28 // [32] Operator          ::= OperatorName\r
29 //                            | MultiplyOperator\r
30 //                            | '/' | '//' | '|' | '+' | '-' | '=' | '!=' | '<' | '<=' | '>' | '>='\r
31 // [33] OperatorName      ::= 'and' | 'or' | 'mod' | 'div'\r
32 // [34] MultiplyOperator  ::= '*'\r
33 // [35] FunctionName      ::= QName - NodeType\r
34 // [36] VariableReference ::= '$' QName\r
35 // [37] NameTest          ::= '*'\r
36 //                            | NCName ':' '*'\r
37 //                            | QName\r
38 // [38] NodeType          ::= 'comment'\r
39 //                            | 'text'\r
40 //                            | 'processing-instruction'\r
41 //                            | 'node'\r
42 // [39] ExprWhitespace    ::= S\r
43 \r
44 namespace System.Xml.XPath\r
45 {\r
46         public enum XPathTokenType\r
47         {\r
48                 Start,\r
49                 End,\r
50                 Error,\r
51                 LeftParen,\r
52                 RightParen,\r
53                 LeftBracket,\r
54                 RightBracket,\r
55                 Dot,\r
56                 DotDot,\r
57                 At,\r
58                 Comma,\r
59                 ColonColon,\r
60                 NameTest,\r
61                 NodeType,\r
62                 Operator,\r
63                 FunctionName,\r
64                 AxisName,\r
65                 Literal,\r
66                 Number,\r
67                 VariableReference\r
68         }\r
69 \r
70         public sealed class XPathScanner\r
71         {\r
72                 private string xpath;\r
73                 private int index;\r
74                 private XPathTokenType tokenType;\r
75                 private string value;\r
76                 private XPathTokenType precedingTokenType;\r
77 \r
78                 public XPathScanner (string xpath)\r
79                 {\r
80                         this.xpath = xpath;\r
81                         index = 0;\r
82                         tokenType = XPathTokenType.Start;\r
83                 }\r
84 \r
85                 public XPathTokenType TokenType {\r
86                         get {\r
87                                 return tokenType;\r
88                         }\r
89                 }\r
90 \r
91                 public string Value {\r
92                         get {\r
93                                 return value;\r
94                         }\r
95                 }\r
96 \r
97                 private int Read ()\r
98                 {\r
99                         int c = Peek ();\r
100                         if (c != -1)\r
101                                 MoveNext ();\r
102                         return c;\r
103                 }\r
104 \r
105                 private int Peek ()\r
106                 {\r
107                         if (index < xpath.Length)\r
108                                 return xpath[index];\r
109                         return -1;\r
110                 }\r
111 \r
112                 private int Peek2 ()\r
113                 {\r
114                         if (index + 1 < xpath.Length)\r
115                                 return xpath[index + 1];\r
116                         return -1;\r
117                 }\r
118 \r
119                 private void MoveNext ()\r
120                 {\r
121                         ++index;\r
122                 }\r
123 \r
124                 private void MovePrevious ()\r
125                 {\r
126                         if (index > 0)\r
127                                 --index;\r
128                 }\r
129 \r
130                 public XPathTokenType Scan ()\r
131                 {\r
132                         precedingTokenType = tokenType;\r
133 \r
134                         int c = Read ();\r
135 \r
136                         if (c == -1) {\r
137                                 tokenType = XPathTokenType.End;\r
138                                 value = null;\r
139                         } else if (c != ':' && XmlChar.IsFirstNameChar (c)) {\r
140                                 StringBuilder builder = new StringBuilder ();\r
141                                 builder.Append ((char) c);\r
142                                 while (Peek () != ':' && XmlChar.IsNameChar (Peek ())) {\r
143                                         builder.Append ((char) Read ());\r
144                                 }\r
145                                 if (Peek () == ':' && Peek2 () != ':') {\r
146                                         Read();\r
147                                         if (XmlChar.IsFirstNameChar (Peek ())) {\r
148                                                 builder.Append (':');\r
149                                                 builder.Append ((char) Read ());\r
150                                                 while (XmlChar.IsNameChar (Peek ())) {\r
151                                                         builder.Append ((char) Read ());\r
152                                                 }\r
153                                                 tokenType = XPathTokenType.NameTest;\r
154                                         } else if (Peek () == '*') {\r
155                                                 builder.Append (':');\r
156                                                 builder.Append ((char) Read ());\r
157                                                 tokenType = XPathTokenType.NameTest;\r
158                                                 value = builder.ToString ();\r
159                                                 return tokenType;\r
160                                         } else {\r
161                                                 tokenType = XPathTokenType.Error;\r
162                                                 return tokenType;\r
163                                         }\r
164                                 }\r
165                                 value = builder.ToString ();\r
166                                 if (precedingTokenType != XPathTokenType.Start &&\r
167                                         precedingTokenType != XPathTokenType.At &&\r
168                                         precedingTokenType != XPathTokenType.ColonColon &&\r
169                                         precedingTokenType != XPathTokenType.LeftParen &&\r
170                                         precedingTokenType != XPathTokenType.LeftBracket &&\r
171                                         precedingTokenType != XPathTokenType.Operator)\r
172                                         tokenType = XPathTokenType.Operator;\r
173                                 else if (Peek () == '(') {\r
174                                         if (value == "comment" || \r
175                                                 value == "node" || \r
176                                                 value == "processing-instruction" || \r
177                                                 value == "text")\r
178                                                 tokenType = XPathTokenType.NodeType;\r
179                                         else\r
180                                                 tokenType = XPathTokenType.FunctionName;\r
181                                 } else {\r
182                                         if (Peek () == ':' && Peek2 () == ':')\r
183                                                 tokenType = XPathTokenType.AxisName;\r
184                                         else\r
185                                                 tokenType = XPathTokenType.NameTest;\r
186                                 }\r
187                                 value = builder.ToString ();\r
188                         } else {\r
189                                 switch (c) {\r
190                                 case '(':\r
191                                         tokenType = XPathTokenType.LeftParen;\r
192                                         value = "(";\r
193                                         break;\r
194                                 case ')':\r
195                                         tokenType = XPathTokenType.RightParen;\r
196                                         value = ")";\r
197                                         break;\r
198                                 case '[':\r
199                                         tokenType = XPathTokenType.LeftBracket;\r
200                                         break;\r
201                                 case ']':\r
202                                         tokenType = XPathTokenType.RightBracket;\r
203                                         break;\r
204                                 case '.':\r
205                                         if (Peek () != '.') {\r
206                                                 tokenType = XPathTokenType.Dot;\r
207                                                 value = ".";\r
208                                         } else {\r
209                                                 Read ();\r
210                                                 tokenType = XPathTokenType.DotDot;\r
211                                                 value = "..";\r
212                                         }\r
213                                         break;\r
214                                 case '@':\r
215                                         tokenType = XPathTokenType.At;\r
216                                         value = "@";\r
217                                         break;\r
218                                 case ',':\r
219                                         tokenType = XPathTokenType.Comma;\r
220                                         break;\r
221                                 case ':':\r
222                                         if (Peek () == ':') {\r
223                                                 Read ();\r
224                                                 tokenType = XPathTokenType.ColonColon;\r
225                                                 value = "::";\r
226                                         } else\r
227                                                 tokenType = XPathTokenType.Error;\r
228                                         break;\r
229                                 case '*':\r
230                                         if (precedingTokenType != XPathTokenType.Start &&\r
231                                                 precedingTokenType != XPathTokenType.At &&\r
232                                                 precedingTokenType != XPathTokenType.ColonColon &&\r
233                                                 precedingTokenType != XPathTokenType.LeftParen &&\r
234                                                 precedingTokenType != XPathTokenType.LeftBracket &&\r
235                                                 precedingTokenType != XPathTokenType.Operator) {\r
236                                                 tokenType = XPathTokenType.Operator;\r
237                                                 value = "*";\r
238                                         } else {\r
239                                                 tokenType = XPathTokenType.NameTest;\r
240                                                 value = "*";\r
241                                         }\r
242                                         break;\r
243                                 default:\r
244                                         if (c == '/') {\r
245                                                 tokenType = XPathTokenType.Operator;\r
246                                                 if (Peek () != '/')\r
247                                                         value = "/";\r
248                                                 else {\r
249                                                         Read ();\r
250                                                         value = "//";\r
251                                                 }\r
252                                         }\r
253                                         break;\r
254                                 }\r
255                         }\r
256 \r
257                         return tokenType;\r
258                 }\r
259         }\r
260 }\r