XPathScanner.cs 6.3 KB

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