PageRenderTime 44ms CodeModel.GetById 15ms RepoModel.GetById 1ms app.codeStats 0ms

/branches/develop/Ivony.Html/Selectors/CssParser.cs

#
C# | 520 lines | 317 code | 127 blank | 76 comment | 127 complexity | 96f36d61cf49c6d18d57d8dc8b9ecc98 MD5 | raw file
  1. using System;
  2. using System.Collections;
  3. using System.Collections.Generic;
  4. using System.Linq;
  5. using System.Text;
  6. using Ivony.Fluent;
  7. namespace Ivony.Html
  8. {
  9. public static class CssParser
  10. {
  11. /// <summary>
  12. /// 从选择器表达式创建选择器对象
  13. /// </summary>
  14. /// <param name="expression"></param>
  15. /// <returns></returns>
  16. public static ICssSelector Create( string expression )
  17. {
  18. var selector = selectorsCache[expression] as ICssSelector;
  19. if ( selector != null )
  20. return selector;
  21. var enumerator = new CharEnumerator( expression );
  22. selector = ParseSelector( enumerator );
  23. selectorsCache[expression] = selector;
  24. return selector;
  25. }
  26. private static Hashtable selectorsCache = new Hashtable();
  27. /// <summary>
  28. /// 创建层级选择器
  29. /// </summary>
  30. /// <param name="expression">选择器表达式</param>
  31. /// <param name="scope">范畴限定,上溯时不超出此范畴</param>
  32. /// <returns>带范畴限定的层叠选择器</returns>
  33. public static ICssSelector Create( IHtmlContainer scope, string expression )
  34. {
  35. return CssCasecadingSelector.Create( new CssAncetorRelativeSelector( new ContainerRestrict( scope ) ), CssParser.Create( expression ) );
  36. }
  37. /// <summary>
  38. /// 解析并创建选择器对象
  39. /// </summary>
  40. /// <param name="enumerator">用于读取选择器表达式的枚举器</param>
  41. /// <returns>选择器对象</returns>
  42. private static ICssSelector ParseSelector( CharEnumerator enumerator )
  43. {
  44. var selectorList = new List<ICssSelector>();
  45. ICssSelector selector = null;
  46. selector = ParseElementSelector( enumerator );
  47. while ( true )
  48. {
  49. SkipWhiteSpace( enumerator );
  50. if ( enumerator.Current == char.MinValue )
  51. {
  52. if ( selectorList.Any() )
  53. {
  54. if ( selector == null )
  55. throw FormatError( enumerator );
  56. selectorList.Add( selector );
  57. return new CssMultipleSelector( selectorList.ToArray() );
  58. }
  59. else
  60. return selector;
  61. }
  62. var ch = enumerator.Current;
  63. if ( ch == '>' || ch == '+' || ch == '~' )
  64. {
  65. EnsureNext( enumerator );
  66. SkipWhiteSpace( enumerator );
  67. var right = ParseElementSelector( enumerator );
  68. if ( right == null )
  69. throw FormatError( enumerator );
  70. selector = CreateCasecadingSelector( selector, ch, right );
  71. }
  72. else if ( ch == ',' )
  73. {
  74. EnsureNext( enumerator );
  75. SkipWhiteSpace( enumerator );
  76. selectorList.Add( selector );
  77. selector = ParseElementSelector( enumerator );
  78. }
  79. else
  80. {
  81. var right = ParseElementSelector( enumerator );
  82. if ( right == null )
  83. throw FormatError( enumerator );
  84. selector = CreateCasecadingSelector( selector, ' ', right );
  85. }
  86. }
  87. }
  88. private static FormatException FormatError( CharEnumerator enumerator )
  89. {
  90. return new FormatException( string.Format( "意外的字符 '{0}' ,在分析CSS选择器表达式 \"{1}\" 第 {2} 字符处。", enumerator.Current, enumerator.ToString(), enumerator.Offset ) );
  91. }
  92. private static FormatException FormatError( CharEnumerator enumerator, char desired )
  93. {
  94. return new FormatException( string.Format( "意外的字符 '{0}' ,在分析CSS选择器表达式 \"{1}\" 第 {2} 字符处,期望的字符为 '{3} '。", enumerator.Current, enumerator.ToString(), enumerator.Offset, desired ) );
  95. }
  96. /// <summary>
  97. /// 创建层叠选择器
  98. /// </summary>
  99. /// <param name="leftSelector">左选择器</param>
  100. /// <param name="combanitor">结合符</param>
  101. /// <param name="rightSelector">右选择器</param>
  102. /// <returns>层叠选择器</returns>
  103. private static ICssSelector CreateCasecadingSelector( ICssSelector leftSelector, char combanitor, CssElementSelector rightSelector )
  104. {
  105. return CssCasecadingSelector.Create( leftSelector, combanitor, rightSelector );
  106. }
  107. /// <summary>
  108. /// 跳过当前位置所有的空白字符
  109. /// </summary>
  110. /// <param name="enumerator">字符枚举器</param>
  111. /// <returns></returns>
  112. private static bool SkipWhiteSpace( CharEnumerator enumerator )
  113. {
  114. var ch = enumerator.Current;
  115. if ( ch == '\u0020' || ch == '\u0009' || ch == '\u000A' || ch == '\u000D' || ch == '\u000C' )
  116. {
  117. while ( enumerator.MoveNext() )
  118. {
  119. ch = enumerator.Current;
  120. if ( ch == '\u0020' || ch == '\u0009' || ch == '\u000A' || ch == '\u000D' || ch == '\u000C' )
  121. continue;
  122. break;
  123. }
  124. return true;
  125. }
  126. else
  127. return false;
  128. }
  129. /// <summary>
  130. /// 解析元素选择器
  131. /// </summary>
  132. /// <param name="enumerator"></param>
  133. /// <returns></returns>
  134. private static CssElementSelector ParseElementSelector( CharEnumerator enumerator )
  135. {
  136. var elementName = ParseName( enumerator );
  137. if ( elementName == null && enumerator.Current == '*' )
  138. {
  139. enumerator.MoveNext();
  140. elementName = "*";
  141. }
  142. var attributeSelectors = new List<CssAttributeSelector>();
  143. var psedoclassSelectors = new List<ICssPseudoClassSelector>();
  144. while ( true )//解析ID和类选择符
  145. {
  146. if ( enumerator.Current == '#' )
  147. {
  148. EnsureNext( enumerator );
  149. attributeSelectors.Add( CreateAttributeSelector( "id", "=", ParseName( enumerator ) ) );
  150. }
  151. else if ( enumerator.Current == '.' )
  152. {
  153. EnsureNext( enumerator );
  154. attributeSelectors.Add( CreateAttributeSelector( "class", "~=", ParseName( enumerator ) ) );
  155. }
  156. else
  157. break;
  158. }
  159. while ( true )//解析属性选择符
  160. {
  161. if ( enumerator.Current == '[' )
  162. attributeSelectors.Add( ParseAttributeSelector( enumerator ) );
  163. else
  164. break;
  165. }
  166. while ( true )//解析伪类选择符
  167. {
  168. if ( enumerator.Current == ':' )
  169. psedoclassSelectors.Add( ParsePsedoclassSelector( enumerator ) );
  170. else
  171. break;
  172. }
  173. if ( elementName != null || attributeSelectors.Any() || psedoclassSelectors.Any() )
  174. return CreateElementSelector( elementName, attributeSelectors.ToArray(), psedoclassSelectors.ToArray() );
  175. else
  176. return null;
  177. }
  178. /// <summary>
  179. /// 创建元素选择器
  180. /// </summary>
  181. /// <param name="elementName">元素名</param>
  182. /// <param name="cssAttributeSelectors">CSS属性选择器</param>
  183. /// <param name="cssPseudoClassSelectors">CSS伪类选择器</param>
  184. /// <returns></returns>
  185. private static CssElementSelector CreateElementSelector( string elementName, CssAttributeSelector[] cssAttributeSelectors, ICssPseudoClassSelector[] cssPseudoClassSelectors )
  186. {
  187. return new CssElementSelector( elementName, cssAttributeSelectors, cssPseudoClassSelectors );
  188. }
  189. /// <summary>
  190. /// 解析属性选择器
  191. /// </summary>
  192. /// <param name="enumerator">字符枚举器</param>
  193. /// <returns></returns>
  194. private static CssAttributeSelector ParseAttributeSelector( CharEnumerator enumerator )
  195. {
  196. if ( enumerator.Current != '[' )
  197. return null;
  198. EnsureNext( enumerator );
  199. var attriuteName = ParseName( enumerator );
  200. var ch = enumerator.Current;
  201. if ( ch == ']' )
  202. {
  203. enumerator.MoveNext();
  204. return CreateAttributeSelector( attriuteName );
  205. }
  206. string compare;
  207. if ( ch == '=' )
  208. compare = "=";
  209. else if ( ch == '^' || ch == '$' || ch == '~' || ch == '*' || ch == '!' )
  210. {
  211. if ( EnsureNext( enumerator ) != '=' )//比较符不正确
  212. throw FormatError( enumerator, '=' );
  213. compare = ch + "=";
  214. }
  215. else
  216. throw FormatError( enumerator );
  217. EnsureNext( enumerator );//比较符后面没有字符
  218. var value = ParseQuoteText( enumerator );//尝试解析引用字符串
  219. if ( value == null )
  220. {
  221. var offset = enumerator.Offset;
  222. while ( true )
  223. {
  224. if ( enumerator.Current == ']' )//遇到结束符
  225. break;
  226. EnsureNext( enumerator );//遇到结束符前结束
  227. }
  228. value = enumerator.SubString( offset, enumerator.Offset - offset );
  229. }
  230. else if ( enumerator.Current != ']' )//引用字符串结束位置不是结束符
  231. throw FormatError( enumerator, ']' );
  232. enumerator.MoveNext();
  233. return CreateAttributeSelector( attriuteName, compare, value );
  234. }
  235. /// <summary>
  236. /// 创建属性选择器
  237. /// </summary>
  238. /// <param name="attriuteName"></param>
  239. /// <returns></returns>
  240. private static CssAttributeSelector CreateAttributeSelector( string attriuteName )
  241. {
  242. return new CssAttributeSelector( attriuteName, null, null );
  243. }
  244. /// <summary>
  245. /// 创建属性选择器
  246. /// </summary>
  247. /// <param name="attributeName"></param>
  248. /// <param name="compare"></param>
  249. /// <param name="value"></param>
  250. /// <returns></returns>
  251. private static CssAttributeSelector CreateAttributeSelector( string attributeName, string compare, string value )
  252. {
  253. return new CssAttributeSelector( attributeName, compare, value );
  254. }
  255. /// <summary>
  256. /// 解析伪类选择器
  257. /// </summary>
  258. /// <param name="enumerator"></param>
  259. /// <returns></returns>
  260. private static ICssPseudoClassSelector ParsePsedoclassSelector( CharEnumerator enumerator )
  261. {
  262. if ( enumerator.Current != ':' )
  263. return null;
  264. EnsureNext( enumerator );
  265. var startOffset = enumerator.Offset;
  266. var name = ParseName( enumerator );
  267. if ( enumerator.Current != '(' )
  268. throw FormatError( enumerator, '(' );
  269. var i = 1;
  270. var argsOffset = enumerator.Offset + 1;
  271. while ( enumerator.MoveNext() )
  272. {
  273. if ( enumerator.Current == '(' )
  274. i++;
  275. else if ( enumerator.Current == ')' )
  276. i--;
  277. if ( i == 0 )
  278. {
  279. enumerator.MoveNext();
  280. var args = enumerator.SubString( argsOffset, enumerator.Offset - argsOffset - 1 );
  281. var expression = enumerator.SubString( startOffset, enumerator.Offset - startOffset );
  282. return CssPseudoClassSelectors.Create( name, args, expression );
  283. }
  284. }
  285. throw new FormatException( "意外的遇到字符串的结束" );
  286. }
  287. /// <summary>
  288. /// 确保当前位置尚未达到字符串末尾
  289. /// </summary>
  290. /// <param name="enumerator"></param>
  291. private static char EnsureNext( CharEnumerator enumerator )
  292. {
  293. if ( !enumerator.MoveNext() )
  294. throw new FormatException( "解析选择器时意外的遇到字符串末尾" );
  295. return enumerator.Current;
  296. }
  297. /// <summary>
  298. /// 解析引用字符串
  299. /// </summary>
  300. /// <param name="enumerator"></param>
  301. /// <returns></returns>
  302. private static string ParseQuoteText( CharEnumerator enumerator )
  303. {
  304. var quoteCharactor = enumerator.Current;
  305. if ( quoteCharactor != '\'' && quoteCharactor != '\"' )
  306. return null;
  307. var offset = enumerator.Offset + 1;
  308. while ( true )
  309. {
  310. EnsureNext( enumerator );
  311. if ( enumerator.Current == quoteCharactor )
  312. {
  313. enumerator.MoveNext();
  314. return enumerator.SubString( offset, enumerator.Offset - offset - 1 );
  315. }
  316. }
  317. }
  318. /// <summary>
  319. /// 解析名称
  320. /// </summary>
  321. /// <param name="enumerator"></param>
  322. /// <returns></returns>
  323. private unsafe static string ParseName( CharEnumerator enumerator )
  324. {
  325. char* buffer = stackalloc char[100];
  326. int i = 0;
  327. do
  328. {
  329. var ch = enumerator.Current;
  330. if ( ch >= 'a' && ch <= 'z' || ch >= 'A' && ch <= 'Z' || ch >= '0' && ch <= '9' || ch == '-' )
  331. buffer[i++] = ch;
  332. else if ( ch == '|' )
  333. buffer[i++] = ':';
  334. else
  335. break;
  336. } while ( enumerator.MoveNext() );
  337. if ( i == 0 )
  338. return null;
  339. else
  340. return new string( buffer, 0, i );
  341. }
  342. private class CharEnumerator : IEnumerator<char>
  343. {
  344. private string _str;
  345. public CharEnumerator( string str )
  346. {
  347. _str = str;
  348. }
  349. private int _index;
  350. public char Current
  351. {
  352. get
  353. {
  354. if ( _index == _str.Length )
  355. return char.MinValue;
  356. return _str[_index];
  357. }
  358. }
  359. public void Dispose()
  360. {
  361. }
  362. object System.Collections.IEnumerator.Current
  363. {
  364. get { return Current; }
  365. }
  366. public bool MoveNext()
  367. {
  368. _index++;
  369. if ( _index < _str.Length )
  370. return true;
  371. if ( _index > _str.Length )
  372. _index = _str.Length;
  373. return false;
  374. }
  375. public void Reset()
  376. {
  377. _index = 0;
  378. }
  379. public int Offset
  380. {
  381. get { return _index; }
  382. }
  383. public string SubString( int offset, int length )
  384. {
  385. return _str.Substring( offset, length );
  386. }
  387. public override string ToString()
  388. {
  389. return _str;
  390. }
  391. }
  392. }
  393. }