[runtime] Fix DISABLE_REFLECTION_EMIT build.
[mono.git] / docs / HtmlAgilityPack / HtmlNode.cs
1 // HtmlAgilityPack V1.0 - Simon Mourier <simon underscore mourier at hotmail dot com>\r
2 using System;\r
3 using System.Collections;\r
4 using System.Collections.Generic;\r
5 using System.Diagnostics;\r
6 using System.IO;\r
7 using System.Xml;\r
8 using System.Xml.XPath;\r
9 \r
10 namespace HtmlAgilityPack\r
11 {\r
12     /// <summary>\r
13     /// Represents an HTML node.\r
14     /// </summary>\r
15     [DebuggerDisplay("Name: {OriginalName}}")]\r
16     public class HtmlNode : IXPathNavigable\r
17     {\r
18         #region Fields\r
19 \r
20         internal HtmlAttributeCollection _attributes;\r
21         internal HtmlNodeCollection _childnodes;\r
22         internal HtmlNode _endnode;\r
23 \r
24         internal bool _innerchanged;\r
25         internal string _innerhtml;\r
26         internal int _innerlength;\r
27         internal int _innerstartindex;\r
28         internal int _line;\r
29         internal int _lineposition;\r
30         private string _name;\r
31         internal int _namelength;\r
32         internal int _namestartindex;\r
33         internal HtmlNode _nextnode;\r
34         internal HtmlNodeType _nodetype;\r
35         internal bool _outerchanged;\r
36         internal string _outerhtml;\r
37         internal int _outerlength;\r
38         internal int _outerstartindex;\r
39         internal HtmlDocument _ownerdocument;\r
40         internal HtmlNode _parentnode;\r
41         internal HtmlNode _prevnode;\r
42         internal HtmlNode _prevwithsamename;\r
43         internal bool _starttag;\r
44         internal int _streamposition;\r
45 \r
46         #endregion\r
47 \r
48         #region Static Members\r
49 \r
50         /// <summary>\r
51         /// Gets the name of a comment node. It is actually defined as '#comment'.\r
52         /// </summary>\r
53         public static readonly string HtmlNodeTypeNameComment = "#comment";\r
54 \r
55         /// <summary>\r
56         /// Gets the name of the document node. It is actually defined as '#document'.\r
57         /// </summary>\r
58         public static readonly string HtmlNodeTypeNameDocument = "#document";\r
59 \r
60         /// <summary>\r
61         /// Gets the name of a text node. It is actually defined as '#text'.\r
62         /// </summary>\r
63         public static readonly string HtmlNodeTypeNameText = "#text";\r
64 \r
65         /// <summary>\r
66         /// Gets a collection of flags that define specific behaviors for specific element nodes.\r
67         /// The table contains a DictionaryEntry list with the lowercase tag name as the Key, and a combination of HtmlElementFlags as the Value.\r
68         /// </summary>\r
69         public static Hashtable ElementsFlags;\r
70 \r
71         #endregion\r
72 \r
73         #region Constructors\r
74 \r
75         /// <summary>\r
76         /// Initialize HtmlNode. Builds a list of all tags that have special allowances\r
77         /// </summary>\r
78         static HtmlNode()\r
79         {\r
80             // tags whose content may be anything\r
81             ElementsFlags = new Hashtable();\r
82             ElementsFlags.Add("script", HtmlElementFlag.CData);\r
83             ElementsFlags.Add("style", HtmlElementFlag.CData);\r
84             ElementsFlags.Add("noxhtml", HtmlElementFlag.CData);\r
85 \r
86             // tags that can not contain other tags\r
87             ElementsFlags.Add("base", HtmlElementFlag.Empty);\r
88             ElementsFlags.Add("link", HtmlElementFlag.Empty);\r
89             ElementsFlags.Add("meta", HtmlElementFlag.Empty);\r
90             ElementsFlags.Add("isindex", HtmlElementFlag.Empty);\r
91             ElementsFlags.Add("hr", HtmlElementFlag.Empty);\r
92             ElementsFlags.Add("col", HtmlElementFlag.Empty);\r
93             ElementsFlags.Add("img", HtmlElementFlag.Empty);\r
94             ElementsFlags.Add("param", HtmlElementFlag.Empty);\r
95             ElementsFlags.Add("embed", HtmlElementFlag.Empty);\r
96             ElementsFlags.Add("frame", HtmlElementFlag.Empty);\r
97             ElementsFlags.Add("wbr", HtmlElementFlag.Empty);\r
98             ElementsFlags.Add("bgsound", HtmlElementFlag.Empty);\r
99             ElementsFlags.Add("spacer", HtmlElementFlag.Empty);\r
100             ElementsFlags.Add("keygen", HtmlElementFlag.Empty);\r
101             ElementsFlags.Add("area", HtmlElementFlag.Empty);\r
102             ElementsFlags.Add("input", HtmlElementFlag.Empty);\r
103             ElementsFlags.Add("basefont", HtmlElementFlag.Empty);\r
104 \r
105             ElementsFlags.Add("form", HtmlElementFlag.CanOverlap | HtmlElementFlag.Empty);\r
106 \r
107             // they sometimes contain, and sometimes they don 't...\r
108             ElementsFlags.Add("option", HtmlElementFlag.Empty);\r
109 \r
110             // tag whose closing tag is equivalent to open tag:\r
111             // <p>bla</p>bla will be transformed into <p>bla</p>bla\r
112             // <p>bla<p>bla will be transformed into <p>bla<p>bla and not <p>bla></p><p>bla</p> or <p>bla<p>bla</p></p>\r
113             //<br> see above\r
114             ElementsFlags.Add("br", HtmlElementFlag.Empty | HtmlElementFlag.Closed);\r
115             ElementsFlags.Add("p", HtmlElementFlag.Empty | HtmlElementFlag.Closed);\r
116         }\r
117 \r
118         /// <summary>\r
119         /// Initializes HtmlNode, providing type, owner and where it exists in a collection\r
120         /// </summary>\r
121         /// <param name="type"></param>\r
122         /// <param name="ownerdocument"></param>\r
123         /// <param name="index"></param>\r
124         public HtmlNode(HtmlNodeType type, HtmlDocument ownerdocument, int index)\r
125         {\r
126             _nodetype = type;\r
127             _ownerdocument = ownerdocument;\r
128             _outerstartindex = index;\r
129 \r
130             switch (type)\r
131             {\r
132                 case HtmlNodeType.Comment:\r
133                     Name = HtmlNodeTypeNameComment;\r
134                     _endnode = this;\r
135                     break;\r
136 \r
137                 case HtmlNodeType.Document:\r
138                     Name = HtmlNodeTypeNameDocument;\r
139                     _endnode = this;\r
140                     break;\r
141 \r
142                 case HtmlNodeType.Text:\r
143                     Name = HtmlNodeTypeNameText;\r
144                     _endnode = this;\r
145                     break;\r
146             }\r
147 \r
148             if (_ownerdocument._openednodes != null)\r
149             {\r
150                 if (!Closed)\r
151                 {\r
152                     // we use the index as the key\r
153 \r
154                     // -1 means the node comes from public\r
155                     if (-1 != index)\r
156                     {\r
157                         _ownerdocument._openednodes.Add(index, this);\r
158                     }\r
159                 }\r
160             }\r
161 \r
162             if ((-1 != index) || (type == HtmlNodeType.Comment) || (type == HtmlNodeType.Text)) return;\r
163             // innerhtml and outerhtml must be calculated\r
164             _outerchanged = true;\r
165             _innerchanged = true;\r
166         }\r
167 \r
168         #endregion\r
169 \r
170         #region Properties\r
171 \r
172         /// <summary>\r
173         /// Gets the collection of HTML attributes for this node. May not be null.\r
174         /// </summary>\r
175         public HtmlAttributeCollection Attributes\r
176         {\r
177             get\r
178             {\r
179                 if (!HasAttributes)\r
180                 {\r
181                     _attributes = new HtmlAttributeCollection(this);\r
182                 }\r
183                 return _attributes;\r
184             }\r
185             internal set { _attributes = value; }\r
186         }\r
187 \r
188         /// <summary>\r
189         /// Gets all the children of the node.\r
190         /// </summary>\r
191         public HtmlNodeCollection ChildNodes\r
192         {\r
193             get\r
194             {\r
195                 if (_childnodes == null)\r
196                 {\r
197                     _childnodes = new HtmlNodeCollection(this);\r
198                 }\r
199                 return _childnodes;\r
200             }\r
201             internal set { _childnodes = value; }\r
202         }\r
203 \r
204         /// <summary>\r
205         /// Gets a value indicating if this node has been closed or not.\r
206         /// </summary>\r
207         public bool Closed\r
208         {\r
209             get { return (_endnode != null); }\r
210         }\r
211 \r
212         /// <summary>\r
213         /// Gets the collection of HTML attributes for the closing tag. May not be null.\r
214         /// </summary>\r
215         public HtmlAttributeCollection ClosingAttributes\r
216         {\r
217             get\r
218             {\r
219                 if (!HasClosingAttributes)\r
220                 {\r
221                     return new HtmlAttributeCollection(this);\r
222                 }\r
223                 return _endnode.Attributes;\r
224             }\r
225         }\r
226 \r
227         internal HtmlNode EndNode\r
228         {\r
229             get { return _endnode; }\r
230         }\r
231 \r
232         /// <summary>\r
233         /// Gets the first child of the node.\r
234         /// </summary>\r
235         public HtmlNode FirstChild\r
236         {\r
237             get\r
238             {\r
239                 if (!HasChildNodes)\r
240                 {\r
241                     return null;\r
242                 }\r
243                 return _childnodes[0];\r
244             }\r
245         }\r
246 \r
247         /// <summary>\r
248         /// Gets a value indicating whether the current node has any attributes.\r
249         /// </summary>\r
250         public bool HasAttributes\r
251         {\r
252             get\r
253             {\r
254                 if (_attributes == null)\r
255                 {\r
256                     return false;\r
257                 }\r
258 \r
259                 if (_attributes.Count <= 0)\r
260                 {\r
261                     return false;\r
262                 }\r
263                 return true;\r
264             }\r
265         }\r
266 \r
267         /// <summary>\r
268         /// Gets a value indicating whether this node has any child nodes.\r
269         /// </summary>\r
270         public bool HasChildNodes\r
271         {\r
272             get\r
273             {\r
274                 if (_childnodes == null)\r
275                 {\r
276                     return false;\r
277                 }\r
278 \r
279                 if (_childnodes.Count <= 0)\r
280                 {\r
281                     return false;\r
282                 }\r
283                 return true;\r
284             }\r
285         }\r
286 \r
287         /// <summary>\r
288         /// Gets a value indicating whether the current node has any attributes on the closing tag.\r
289         /// </summary>\r
290         public bool HasClosingAttributes\r
291         {\r
292             get\r
293             {\r
294                 if ((_endnode == null) || (_endnode == this))\r
295                 {\r
296                     return false;\r
297                 }\r
298 \r
299                 if (_endnode._attributes == null)\r
300                 {\r
301                     return false;\r
302                 }\r
303 \r
304                 if (_endnode._attributes.Count <= 0)\r
305                 {\r
306                     return false;\r
307                 }\r
308                 return true;\r
309             }\r
310         }\r
311 \r
312         /// <summary>\r
313         /// Gets or sets the value of the 'id' HTML attribute. The document must have been parsed using the OptionUseIdAttribute set to true.\r
314         /// </summary>\r
315         public string Id\r
316         {\r
317             get\r
318             {\r
319                 if (_ownerdocument._nodesid == null)\r
320                 {\r
321                     throw new Exception(HtmlDocument.HtmlExceptionUseIdAttributeFalse);\r
322                 }\r
323                 return GetId();\r
324             }\r
325             set\r
326             {\r
327                 if (_ownerdocument._nodesid == null)\r
328                 {\r
329                     throw new Exception(HtmlDocument.HtmlExceptionUseIdAttributeFalse);\r
330                 }\r
331 \r
332                 if (value == null)\r
333                 {\r
334                     throw new ArgumentNullException("value");\r
335                 }\r
336                 SetId(value);\r
337             }\r
338         }\r
339 \r
340         /// <summary>\r
341         /// Gets or Sets the HTML between the start and end tags of the object.\r
342         /// </summary>\r
343         public virtual string InnerHtml\r
344         {\r
345             get\r
346             {\r
347                 if (_innerchanged)\r
348                 {\r
349                     _innerhtml = WriteContentTo();\r
350                     _innerchanged = false;\r
351                     return _innerhtml;\r
352                 }\r
353                 if (_innerhtml != null)\r
354                 {\r
355                     return _innerhtml;\r
356                 }\r
357 \r
358                 if (_innerstartindex < 0)\r
359                 {\r
360                     return string.Empty;\r
361                 }\r
362 \r
363                 return _ownerdocument._text.Substring(_innerstartindex, _innerlength);\r
364             }\r
365             set\r
366             {\r
367                 HtmlDocument doc = new HtmlDocument();\r
368                 doc.LoadHtml(value);\r
369 \r
370                 RemoveAllChildren();\r
371                 AppendChildren(doc.DocumentNode.ChildNodes);\r
372             }\r
373         }\r
374 \r
375         /// <summary>\r
376         /// Gets or Sets the text between the start and end tags of the object.\r
377         /// </summary>\r
378         public virtual string InnerText\r
379         {\r
380             get\r
381             {\r
382                 if (_nodetype == HtmlNodeType.Text)\r
383                 {\r
384                     return ((HtmlTextNode) this).Text;\r
385                 }\r
386 \r
387                 if (_nodetype == HtmlNodeType.Comment)\r
388                 {\r
389                     return ((HtmlCommentNode) this).Comment;\r
390                 }\r
391 \r
392                 // note: right now, this method is *slow*, because we recompute everything.\r
393                 // it could be optimised like innerhtml\r
394                 if (!HasChildNodes)\r
395                 {\r
396                     return string.Empty;\r
397                 }\r
398 \r
399                 string s = null;\r
400                 foreach (HtmlNode node in ChildNodes)\r
401                 {\r
402                     s += node.InnerText;\r
403                 }\r
404                 return s;\r
405             }\r
406         }\r
407 \r
408         /// <summary>\r
409         /// Gets the last child of the node.\r
410         /// </summary>\r
411         public HtmlNode LastChild\r
412         {\r
413             get\r
414             {\r
415                 return !HasChildNodes ? null : _childnodes[_childnodes.Count - 1];\r
416             }\r
417         }\r
418 \r
419         /// <summary>\r
420         /// Gets the line number of this node in the document.\r
421         /// </summary>\r
422         public int Line\r
423         {\r
424             get { return _line; }\r
425             internal set { _line = value; }\r
426         }\r
427 \r
428         /// <summary>\r
429         /// Gets the column number of this node in the document.\r
430         /// </summary>\r
431         public int LinePosition\r
432         {\r
433             get { return _lineposition; }\r
434             internal set { _lineposition = value; }\r
435         }\r
436 \r
437         /// <summary>\r
438         /// Gets or sets this node's name.\r
439         /// </summary>\r
440         public string Name\r
441         {\r
442             get\r
443             {\r
444                 if (_name == null)\r
445                 {\r
446                     Name = _ownerdocument._text.Substring(_namestartindex, _namelength);\r
447                 }\r
448                 return _name != null ? _name.ToLower() : string.Empty;\r
449             }\r
450             set { _name = value; }\r
451         }\r
452 \r
453         /// <summary>\r
454         /// Gets the HTML node immediately following this element.\r
455         /// </summary>\r
456         public HtmlNode NextSibling\r
457         {\r
458             get { return _nextnode; }\r
459             internal set { _nextnode = value; }\r
460         }\r
461 \r
462         /// <summary>\r
463         /// Gets the type of this node.\r
464         /// </summary>\r
465         public HtmlNodeType NodeType\r
466         {\r
467             get { return _nodetype; }\r
468             internal set { _nodetype = value; }\r
469         }\r
470 \r
471         /// <summary>\r
472         /// The original unaltered name of the tag\r
473         /// </summary>\r
474         public string OriginalName\r
475         {\r
476             get { return _name; }\r
477         }\r
478 \r
479         /// <summary>\r
480         /// Gets or Sets the object and its content in HTML.\r
481         /// </summary>\r
482         public virtual string OuterHtml\r
483         {\r
484             get\r
485             {\r
486                 if (_outerchanged)\r
487                 {\r
488                     _outerhtml = WriteTo();\r
489                     _outerchanged = false;\r
490                     return _outerhtml;\r
491                 }\r
492 \r
493                 if (_outerhtml != null)\r
494                 {\r
495                     return _outerhtml;\r
496                 }\r
497 \r
498                 if (_outerstartindex < 0)\r
499                 {\r
500                     return string.Empty;\r
501                 }\r
502 \r
503                 return _ownerdocument._text.Substring(_outerstartindex, _outerlength);\r
504             }\r
505         }\r
506 \r
507         /// <summary>\r
508         /// Gets the <see cref="HtmlDocument"/> to which this node belongs.\r
509         /// </summary>\r
510         public HtmlDocument OwnerDocument\r
511         {\r
512             get { return _ownerdocument; }\r
513             internal set { _ownerdocument = value; }\r
514         }\r
515 \r
516         /// <summary>\r
517         /// Gets the parent of this node (for nodes that can have parents).\r
518         /// </summary>\r
519         public HtmlNode ParentNode\r
520         {\r
521             get { return _parentnode; }\r
522             internal set { _parentnode = value; }\r
523         }\r
524 \r
525         /// <summary>\r
526         /// Gets the node immediately preceding this node.\r
527         /// </summary>\r
528         public HtmlNode PreviousSibling\r
529         {\r
530             get { return _prevnode; }\r
531             internal set { _prevnode = value; }\r
532         }\r
533 \r
534         /// <summary>\r
535         /// Gets the stream position of this node in the document, relative to the start of the document.\r
536         /// </summary>\r
537         public int StreamPosition\r
538         {\r
539             get { return _streamposition; }\r
540         }\r
541 \r
542         /// <summary>\r
543         /// Gets a valid XPath string that points to this node\r
544         /// </summary>\r
545         public string XPath\r
546         {\r
547             get\r
548             {\r
549                 string basePath = (ParentNode == null || ParentNode.NodeType == HtmlNodeType.Document)\r
550                                       ? "/"\r
551                                       : ParentNode.XPath + "/";\r
552                 return basePath + GetRelativeXpath();\r
553             }\r
554         }\r
555 \r
556         #endregion\r
557 \r
558         #region IXPathNavigable Members\r
559 \r
560         /// <summary>\r
561         /// Creates a new XPathNavigator object for navigating this HTML node.\r
562         /// </summary>\r
563         /// <returns>An XPathNavigator object. The XPathNavigator is positioned on the node from which the method was called. It is not positioned on the root of the document.</returns>\r
564         public XPathNavigator CreateNavigator()\r
565         {\r
566             return new HtmlNodeNavigator(_ownerdocument, this);\r
567         }\r
568 \r
569         #endregion\r
570 \r
571         #region Public Methods\r
572 \r
573         /// <summary>\r
574         /// Determines if an element node can be kept overlapped.\r
575         /// </summary>\r
576         /// <param name="name">The name of the element node to check. May not be <c>null</c>.</param>\r
577         /// <returns>true if the name is the name of an element node that can be kept overlapped, <c>false</c> otherwise.</returns>\r
578         public static bool CanOverlapElement(string name)\r
579         {\r
580             if (name == null)\r
581             {\r
582                 throw new ArgumentNullException("name");\r
583             }\r
584 \r
585             object flag = ElementsFlags[name.ToLower()];\r
586             if (flag == null)\r
587             {\r
588                 return false;\r
589             }\r
590             return (((HtmlElementFlag) flag) & HtmlElementFlag.CanOverlap) != 0;\r
591         }\r
592 \r
593         /// <summary>\r
594         /// Creates an HTML node from a string representing literal HTML.\r
595         /// </summary>\r
596         /// <param name="html">The HTML text.</param>\r
597         /// <returns>The newly created node instance.</returns>\r
598         public static HtmlNode CreateNode(string html)\r
599         {\r
600             // REVIEW: this is *not* optimum...\r
601             HtmlDocument doc = new HtmlDocument();\r
602             doc.LoadHtml(html);\r
603             return doc.DocumentNode.FirstChild;\r
604         }\r
605 \r
606         /// <summary>\r
607         /// Determines if an element node is a CDATA element node.\r
608         /// </summary>\r
609         /// <param name="name">The name of the element node to check. May not be null.</param>\r
610         /// <returns>true if the name is the name of a CDATA element node, false otherwise.</returns>\r
611         public static bool IsCDataElement(string name)\r
612         {\r
613             if (name == null)\r
614             {\r
615                 throw new ArgumentNullException("name");\r
616             }\r
617 \r
618             object flag = ElementsFlags[name.ToLower()];\r
619             if (flag == null)\r
620             {\r
621                 return false;\r
622             }\r
623             return (((HtmlElementFlag) flag) & HtmlElementFlag.CData) != 0;\r
624         }\r
625 \r
626         /// <summary>\r
627         /// Determines if an element node is closed.\r
628         /// </summary>\r
629         /// <param name="name">The name of the element node to check. May not be null.</param>\r
630         /// <returns>true if the name is the name of a closed element node, false otherwise.</returns>\r
631         public static bool IsClosedElement(string name)\r
632         {\r
633             if (name == null)\r
634             {\r
635                 throw new ArgumentNullException("name");\r
636             }\r
637 \r
638             object flag = ElementsFlags[name.ToLower()];\r
639             if (flag == null)\r
640             {\r
641                 return false;\r
642             }\r
643             return (((HtmlElementFlag) flag) & HtmlElementFlag.Closed) != 0;\r
644         }\r
645 \r
646         /// <summary>\r
647         /// Determines if an element node is defined as empty.\r
648         /// </summary>\r
649         /// <param name="name">The name of the element node to check. May not be null.</param>\r
650         /// <returns>true if the name is the name of an empty element node, false otherwise.</returns>\r
651         public static bool IsEmptyElement(string name)\r
652         {\r
653             if (name == null)\r
654             {\r
655                 throw new ArgumentNullException("name");\r
656             }\r
657 \r
658             if (name.Length == 0)\r
659             {\r
660                 return true;\r
661             }\r
662 \r
663             // <!DOCTYPE ...\r
664             if ('!' == name[0])\r
665             {\r
666                 return true;\r
667             }\r
668 \r
669             // <?xml ...\r
670             if ('?' == name[0])\r
671             {\r
672                 return true;\r
673             }\r
674 \r
675             object flag = ElementsFlags[name.ToLower()];\r
676             if (flag == null)\r
677             {\r
678                 return false;\r
679             }\r
680             return (((HtmlElementFlag) flag) & HtmlElementFlag.Empty) != 0;\r
681         }\r
682 \r
683         /// <summary>\r
684         /// Determines if a text corresponds to the closing tag of an node that can be kept overlapped.\r
685         /// </summary>\r
686         /// <param name="text">The text to check. May not be null.</param>\r
687         /// <returns>true or false.</returns>\r
688         public static bool IsOverlappedClosingElement(string text)\r
689         {\r
690             if (text == null)\r
691             {\r
692                 throw new ArgumentNullException("text");\r
693             }\r
694             // min is </x>: 4\r
695             if (text.Length <= 4)\r
696                 return false;\r
697 \r
698             if ((text[0] != '<') ||\r
699                 (text[text.Length - 1] != '>') ||\r
700                 (text[1] != '/'))\r
701                 return false;\r
702 \r
703             string name = text.Substring(2, text.Length - 3);\r
704             return CanOverlapElement(name);\r
705         }\r
706 \r
707         /// <summary>\r
708         /// Returns a collection of all ancestor nodes of this element.\r
709         /// </summary>\r
710         /// <returns></returns>\r
711         public IEnumerable<HtmlNode> Ancestors()\r
712         {\r
713             HtmlNode node = ParentNode;\r
714             while (node.ParentNode != null)\r
715             {\r
716                 yield return node.ParentNode;\r
717                 node = node.ParentNode;\r
718             }\r
719         }\r
720 \r
721         /// <summary>\r
722         /// Get Ancestors with matching name\r
723         /// </summary>\r
724         /// <param name="name"></param>\r
725         /// <returns></returns>\r
726         public IEnumerable<HtmlNode> Ancestors(string name)\r
727         {\r
728             for (HtmlNode n = ParentNode; n != null; n = n.ParentNode)\r
729                 if (n.Name == name)\r
730                     yield return n;\r
731         }\r
732 \r
733         /// <summary>\r
734         /// Returns a collection of all ancestor nodes of this element.\r
735         /// </summary>\r
736         /// <returns></returns>\r
737         public IEnumerable<HtmlNode> AncestorsAndSelf()\r
738         {\r
739             for (HtmlNode n = this; n != null; n = n.ParentNode)\r
740                 yield return n;\r
741         }\r
742 \r
743         /// <summary>\r
744         /// Gets all anscestor nodes and the current node\r
745         /// </summary>\r
746         /// <param name="name"></param>\r
747         /// <returns></returns>\r
748         public IEnumerable<HtmlNode> AncestorsAndSelf(string name)\r
749         {\r
750             for (HtmlNode n = this; n != null; n = n.ParentNode)\r
751                 if (n.Name == name)\r
752                     yield return n;\r
753         }\r
754 \r
755         /// <summary>\r
756         /// Adds the specified node to the end of the list of children of this node.\r
757         /// </summary>\r
758         /// <param name="newChild">The node to add. May not be null.</param>\r
759         /// <returns>The node added.</returns>\r
760         public HtmlNode AppendChild(HtmlNode newChild)\r
761         {\r
762             if (newChild == null)\r
763             {\r
764                 throw new ArgumentNullException("newChild");\r
765             }\r
766 \r
767             ChildNodes.Append(newChild);\r
768             _ownerdocument.SetIdForNode(newChild, newChild.GetId());\r
769             _outerchanged = true;\r
770             _innerchanged = true;\r
771             return newChild;\r
772         }\r
773 \r
774         /// <summary>\r
775         /// Adds the specified node to the end of the list of children of this node.\r
776         /// </summary>\r
777         /// <param name="newChildren">The node list to add. May not be null.</param>\r
778         public void AppendChildren(HtmlNodeCollection newChildren)\r
779         {\r
780             if (newChildren == null)\r
781                 throw new ArgumentNullException("newChildrend");\r
782 \r
783             foreach (HtmlNode newChild in newChildren)\r
784             {\r
785                 AppendChild(newChild);\r
786             }\r
787         }\r
788 \r
789         /// <summary>\r
790         /// Gets all Attributes with name\r
791         /// </summary>\r
792         /// <param name="name"></param>\r
793         /// <returns></returns>\r
794         public IEnumerable<HtmlAttribute> ChildAttributes(string name)\r
795         {\r
796             return Attributes.AttributesWithName(name);\r
797         }\r
798 \r
799         /// <summary>\r
800         /// Creates a duplicate of the node\r
801         /// </summary>\r
802         /// <returns></returns>\r
803         public HtmlNode Clone()\r
804         {\r
805             return CloneNode(true);\r
806         }\r
807 \r
808         /// <summary>\r
809         /// Creates a duplicate of the node and changes its name at the same time.\r
810         /// </summary>\r
811         /// <param name="newName">The new name of the cloned node. May not be <c>null</c>.</param>\r
812         /// <returns>The cloned node.</returns>\r
813         public HtmlNode CloneNode(string newName)\r
814         {\r
815             return CloneNode(newName, true);\r
816         }\r
817 \r
818         /// <summary>\r
819         /// Creates a duplicate of the node and changes its name at the same time.\r
820         /// </summary>\r
821         /// <param name="newName">The new name of the cloned node. May not be null.</param>\r
822         /// <param name="deep">true to recursively clone the subtree under the specified node; false to clone only the node itself.</param>\r
823         /// <returns>The cloned node.</returns>\r
824         public HtmlNode CloneNode(string newName, bool deep)\r
825         {\r
826             if (newName == null)\r
827             {\r
828                 throw new ArgumentNullException("newName");\r
829             }\r
830 \r
831             HtmlNode node = CloneNode(deep);\r
832             node.Name = newName;\r
833             return node;\r
834         }\r
835 \r
836         /// <summary>\r
837         /// Creates a duplicate of the node.\r
838         /// </summary>\r
839         /// <param name="deep">true to recursively clone the subtree under the specified node; false to clone only the node itself.</param>\r
840         /// <returns>The cloned node.</returns>\r
841         public HtmlNode CloneNode(bool deep)\r
842         {\r
843             HtmlNode node = _ownerdocument.CreateNode(_nodetype);\r
844             node.Name = Name;\r
845 \r
846             switch (_nodetype)\r
847             {\r
848                 case HtmlNodeType.Comment:\r
849                     ((HtmlCommentNode) node).Comment = ((HtmlCommentNode) this).Comment;\r
850                     return node;\r
851 \r
852                 case HtmlNodeType.Text:\r
853                     ((HtmlTextNode) node).Text = ((HtmlTextNode) this).Text;\r
854                     return node;\r
855             }\r
856 \r
857             // attributes\r
858             if (HasAttributes)\r
859             {\r
860                 foreach (HtmlAttribute att in _attributes)\r
861                 {\r
862                     HtmlAttribute newatt = att.Clone();\r
863                     node.Attributes.Append(newatt);\r
864                 }\r
865             }\r
866 \r
867             // closing attributes\r
868             if (HasClosingAttributes)\r
869             {\r
870                 node._endnode = _endnode.CloneNode(false);\r
871                 foreach (HtmlAttribute att in _endnode._attributes)\r
872                 {\r
873                     HtmlAttribute newatt = att.Clone();\r
874                     node._endnode._attributes.Append(newatt);\r
875                 }\r
876             }\r
877             if (!deep)\r
878             {\r
879                 return node;\r
880             }\r
881 \r
882             if (!HasChildNodes)\r
883             {\r
884                 return node;\r
885             }\r
886 \r
887             // child nodes\r
888             foreach (HtmlNode child in _childnodes)\r
889             {\r
890                 HtmlNode newchild = child.Clone();\r
891                 node.AppendChild(newchild);\r
892             }\r
893             return node;\r
894         }\r
895 \r
896         /// <summary>\r
897         /// Creates a duplicate of the node and the subtree under it.\r
898         /// </summary>\r
899         /// <param name="node">The node to duplicate. May not be <c>null</c>.</param>\r
900         public void CopyFrom(HtmlNode node)\r
901         {\r
902             CopyFrom(node, true);\r
903         }\r
904 \r
905         /// <summary>\r
906         /// Creates a duplicate of the node.\r
907         /// </summary>\r
908         /// <param name="node">The node to duplicate. May not be <c>null</c>.</param>\r
909         /// <param name="deep">true to recursively clone the subtree under the specified node, false to clone only the node itself.</param>\r
910         public void CopyFrom(HtmlNode node, bool deep)\r
911         {\r
912             if (node == null)\r
913             {\r
914                 throw new ArgumentNullException("node");\r
915             }\r
916 \r
917             Attributes.RemoveAll();\r
918             if (node.HasAttributes)\r
919             {\r
920                 foreach (HtmlAttribute att in node.Attributes)\r
921                 {\r
922                     SetAttributeValue(att.Name, att.Value);\r
923                 }\r
924             }\r
925 \r
926             if (!deep)\r
927             {\r
928                 RemoveAllChildren();\r
929                 if (node.HasChildNodes)\r
930                 {\r
931                     foreach (HtmlNode child in node.ChildNodes)\r
932                     {\r
933                         AppendChild(child.CloneNode(true));\r
934                     }\r
935                 }\r
936             }\r
937         }\r
938 \r
939         /// <summary>\r
940         /// Creates an XPathNavigator using the root of this document.\r
941         /// </summary>\r
942         /// <returns></returns>\r
943         public XPathNavigator CreateRootNavigator()\r
944         {\r
945             return new HtmlNodeNavigator(_ownerdocument, _ownerdocument.DocumentNode);\r
946         }\r
947 \r
948         /// <summary>\r
949         /// Gets all Descendant nodes for this node and each of child nodes\r
950         /// </summary>\r
951         /// <returns></returns>\r
952         public IEnumerable<HtmlNode> DescendantNodes()\r
953         {\r
954             foreach (HtmlNode node in ChildNodes)\r
955             {\r
956                 yield return node;\r
957                 foreach (HtmlNode descendant in node.DescendantNodes())\r
958                     yield return descendant;\r
959             }\r
960         }\r
961 \r
962         /// <summary>\r
963         /// Returns a collection of all descendant nodes of this element, in document order\r
964         /// </summary>\r
965         /// <returns></returns>\r
966         public IEnumerable<HtmlNode> DescendantNodesAndSelf()\r
967         {\r
968             return DescendantsAndSelf();\r
969         }\r
970 \r
971         /// <summary>\r
972         /// Gets all Descendant nodes in enumerated list\r
973         /// </summary>\r
974         /// <returns></returns>\r
975         public IEnumerable<HtmlNode> Descendants()\r
976         {\r
977             foreach (HtmlNode node in DescendantNodes())\r
978             {\r
979                 yield return node;\r
980             }\r
981         }\r
982 \r
983         /// <summary>\r
984         /// Get all descendant nodes with matching name\r
985         /// </summary>\r
986         /// <param name="name"></param>\r
987         /// <returns></returns>\r
988         public IEnumerable<HtmlNode> Descendants(string name)\r
989         {\r
990             foreach (HtmlNode node in Descendants())\r
991                 if (node.Name == name)\r
992                     yield return node;\r
993         }\r
994 \r
995         /// <summary>\r
996         /// Returns a collection of all descendant nodes of this element, in document order\r
997         /// </summary>\r
998         /// <returns></returns>\r
999         public IEnumerable<HtmlNode> DescendantsAndSelf()\r
1000         {\r
1001             yield return this;\r
1002             foreach (HtmlNode n in DescendantNodes())\r
1003             {\r
1004                 HtmlNode el = n;\r
1005                 if (el != null)\r
1006                     yield return el;\r
1007             }\r
1008         }\r
1009 \r
1010         /// <summary>\r
1011         /// Gets all descendant nodes including this node\r
1012         /// </summary>\r
1013         /// <param name="name"></param>\r
1014         /// <returns></returns>\r
1015         public IEnumerable<HtmlNode> DescendantsAndSelf(string name)\r
1016         {\r
1017             yield return this;\r
1018             foreach (HtmlNode node in Descendants())\r
1019                 if (node.Name == name)\r
1020                     yield return node;\r
1021         }\r
1022 \r
1023         /// <summary>\r
1024         /// Gets first generation child node matching name\r
1025         /// </summary>\r
1026         /// <param name="name"></param>\r
1027         /// <returns></returns>\r
1028         public HtmlNode Element(string name)\r
1029         {\r
1030             foreach (HtmlNode node in ChildNodes)\r
1031                 if (node.Name == name)\r
1032                     return node;\r
1033             return null;\r
1034         }\r
1035 \r
1036         /// <summary>\r
1037         /// Gets matching first generation child nodes matching name\r
1038         /// </summary>\r
1039         /// <param name="name"></param>\r
1040         /// <returns></returns>\r
1041         public IEnumerable<HtmlNode> Elements(string name)\r
1042         {\r
1043             foreach (HtmlNode node in ChildNodes)\r
1044                 if (node.Name == name)\r
1045                     yield return node;\r
1046         }\r
1047 \r
1048         /// <summary>\r
1049         /// Helper method to get the value of an attribute of this node. If the attribute is not found, the default value will be returned.\r
1050         /// </summary>\r
1051         /// <param name="name">The name of the attribute to get. May not be <c>null</c>.</param>\r
1052         /// <param name="def">The default value to return if not found.</param>\r
1053         /// <returns>The value of the attribute if found, the default value if not found.</returns>\r
1054         public string GetAttributeValue(string name, string def)\r
1055         {\r
1056             if (name == null)\r
1057             {\r
1058                 throw new ArgumentNullException("name");\r
1059             }\r
1060 \r
1061             if (!HasAttributes)\r
1062             {\r
1063                 return def;\r
1064             }\r
1065             HtmlAttribute att = Attributes[name];\r
1066             if (att == null)\r
1067             {\r
1068                 return def;\r
1069             }\r
1070             return att.Value;\r
1071         }\r
1072 \r
1073         /// <summary>\r
1074         /// Helper method to get the value of an attribute of this node. If the attribute is not found, the default value will be returned.\r
1075         /// </summary>\r
1076         /// <param name="name">The name of the attribute to get. May not be <c>null</c>.</param>\r
1077         /// <param name="def">The default value to return if not found.</param>\r
1078         /// <returns>The value of the attribute if found, the default value if not found.</returns>\r
1079         public int GetAttributeValue(string name, int def)\r
1080         {\r
1081             if (name == null)\r
1082             {\r
1083                 throw new ArgumentNullException("name");\r
1084             }\r
1085 \r
1086             if (!HasAttributes)\r
1087             {\r
1088                 return def;\r
1089             }\r
1090             HtmlAttribute att = Attributes[name];\r
1091             if (att == null)\r
1092             {\r
1093                 return def;\r
1094             }\r
1095             try\r
1096             {\r
1097                 return Convert.ToInt32(att.Value);\r
1098             }\r
1099             catch\r
1100             {\r
1101                 return def;\r
1102             }\r
1103         }\r
1104 \r
1105         /// <summary>\r
1106         /// Helper method to get the value of an attribute of this node. If the attribute is not found, the default value will be returned.\r
1107         /// </summary>\r
1108         /// <param name="name">The name of the attribute to get. May not be <c>null</c>.</param>\r
1109         /// <param name="def">The default value to return if not found.</param>\r
1110         /// <returns>The value of the attribute if found, the default value if not found.</returns>\r
1111         public bool GetAttributeValue(string name, bool def)\r
1112         {\r
1113             if (name == null)\r
1114             {\r
1115                 throw new ArgumentNullException("name");\r
1116             }\r
1117 \r
1118             if (!HasAttributes)\r
1119             {\r
1120                 return def;\r
1121             }\r
1122             HtmlAttribute att = Attributes[name];\r
1123             if (att == null)\r
1124             {\r
1125                 return def;\r
1126             }\r
1127             try\r
1128             {\r
1129                 return Convert.ToBoolean(att.Value);\r
1130             }\r
1131             catch\r
1132             {\r
1133                 return def;\r
1134             }\r
1135         }\r
1136 \r
1137         /// <summary>\r
1138         /// Inserts the specified node immediately after the specified reference node.\r
1139         /// </summary>\r
1140         /// <param name="newChild">The node to insert. May not be <c>null</c>.</param>\r
1141         /// <param name="refChild">The node that is the reference node. The newNode is placed after the refNode.</param>\r
1142         /// <returns>The node being inserted.</returns>\r
1143         public HtmlNode InsertAfter(HtmlNode newChild, HtmlNode refChild)\r
1144         {\r
1145             if (newChild == null)\r
1146             {\r
1147                 throw new ArgumentNullException("newChild");\r
1148             }\r
1149 \r
1150             if (refChild == null)\r
1151             {\r
1152                 return PrependChild(newChild);\r
1153             }\r
1154 \r
1155             if (newChild == refChild)\r
1156             {\r
1157                 return newChild;\r
1158             }\r
1159 \r
1160             int index = -1;\r
1161 \r
1162             if (_childnodes != null)\r
1163             {\r
1164                 index = _childnodes[refChild];\r
1165             }\r
1166             if (index == -1)\r
1167             {\r
1168                 throw new ArgumentException(HtmlDocument.HtmlExceptionRefNotChild);\r
1169             }\r
1170 \r
1171             if (_childnodes != null) _childnodes.Insert(index + 1, newChild);\r
1172 \r
1173             _ownerdocument.SetIdForNode(newChild, newChild.GetId());\r
1174             _outerchanged = true;\r
1175             _innerchanged = true;\r
1176             return newChild;\r
1177         }\r
1178 \r
1179         /// <summary>\r
1180         /// Inserts the specified node immediately before the specified reference node.\r
1181         /// </summary>\r
1182         /// <param name="newChild">The node to insert. May not be <c>null</c>.</param>\r
1183         /// <param name="refChild">The node that is the reference node. The newChild is placed before this node.</param>\r
1184         /// <returns>The node being inserted.</returns>\r
1185         public HtmlNode InsertBefore(HtmlNode newChild, HtmlNode refChild)\r
1186         {\r
1187             if (newChild == null)\r
1188             {\r
1189                 throw new ArgumentNullException("newChild");\r
1190             }\r
1191 \r
1192             if (refChild == null)\r
1193             {\r
1194                 return AppendChild(newChild);\r
1195             }\r
1196 \r
1197             if (newChild == refChild)\r
1198             {\r
1199                 return newChild;\r
1200             }\r
1201 \r
1202             int index = -1;\r
1203 \r
1204             if (_childnodes != null)\r
1205             {\r
1206                 index = _childnodes[refChild];\r
1207             }\r
1208 \r
1209             if (index == -1)\r
1210             {\r
1211                 throw new ArgumentException(HtmlDocument.HtmlExceptionRefNotChild);\r
1212             }\r
1213 \r
1214             if (_childnodes != null) _childnodes.Insert(index, newChild);\r
1215 \r
1216             _ownerdocument.SetIdForNode(newChild, newChild.GetId());\r
1217             _outerchanged = true;\r
1218             _innerchanged = true;\r
1219             return newChild;\r
1220         }\r
1221 \r
1222         /// <summary>\r
1223         /// Adds the specified node to the beginning of the list of children of this node.\r
1224         /// </summary>\r
1225         /// <param name="newChild">The node to add. May not be <c>null</c>.</param>\r
1226         /// <returns>The node added.</returns>\r
1227         public HtmlNode PrependChild(HtmlNode newChild)\r
1228         {\r
1229             if (newChild == null)\r
1230             {\r
1231                 throw new ArgumentNullException("newChild");\r
1232             }\r
1233             ChildNodes.Prepend(newChild);\r
1234             _ownerdocument.SetIdForNode(newChild, newChild.GetId());\r
1235             _outerchanged = true;\r
1236             _innerchanged = true;\r
1237             return newChild;\r
1238         }\r
1239 \r
1240         /// <summary>\r
1241         /// Adds the specified node list to the beginning of the list of children of this node.\r
1242         /// </summary>\r
1243         /// <param name="newChildren">The node list to add. May not be <c>null</c>.</param>\r
1244         public void PrependChildren(HtmlNodeCollection newChildren)\r
1245         {\r
1246             if (newChildren == null)\r
1247             {\r
1248                 throw new ArgumentNullException("newChildren");\r
1249             }\r
1250 \r
1251             foreach (HtmlNode newChild in newChildren)\r
1252             {\r
1253                 PrependChild(newChild);\r
1254             }\r
1255         }\r
1256 \r
1257         /// <summary>\r
1258         /// Removes node from parent collection\r
1259         /// </summary>\r
1260         public void Remove()\r
1261         {\r
1262             if (ParentNode != null)\r
1263                 ParentNode.ChildNodes.Remove(this);\r
1264         }\r
1265 \r
1266         /// <summary>\r
1267         /// Removes all the children and/or attributes of the current node.\r
1268         /// </summary>\r
1269         public void RemoveAll()\r
1270         {\r
1271             RemoveAllChildren();\r
1272 \r
1273             if (HasAttributes)\r
1274             {\r
1275                 _attributes.Clear();\r
1276             }\r
1277 \r
1278             if ((_endnode != null) && (_endnode != this))\r
1279             {\r
1280                 if (_endnode._attributes != null)\r
1281                 {\r
1282                     _endnode._attributes.Clear();\r
1283                 }\r
1284             }\r
1285             _outerchanged = true;\r
1286             _innerchanged = true;\r
1287         }\r
1288 \r
1289         /// <summary>\r
1290         /// Removes all the children of the current node.\r
1291         /// </summary>\r
1292         public void RemoveAllChildren()\r
1293         {\r
1294             if (!HasChildNodes)\r
1295             {\r
1296                 return;\r
1297             }\r
1298 \r
1299             if (_ownerdocument.OptionUseIdAttribute)\r
1300             {\r
1301                 // remove nodes from id list\r
1302                 foreach (HtmlNode node in _childnodes)\r
1303                 {\r
1304                     _ownerdocument.SetIdForNode(null, node.GetId());\r
1305                 }\r
1306             }\r
1307             _childnodes.Clear();\r
1308             _outerchanged = true;\r
1309             _innerchanged = true;\r
1310         }\r
1311 \r
1312         /// <summary>\r
1313         /// Removes the specified child node.\r
1314         /// </summary>\r
1315         /// <param name="oldChild">The node being removed. May not be <c>null</c>.</param>\r
1316         /// <returns>The node removed.</returns>\r
1317         public HtmlNode RemoveChild(HtmlNode oldChild)\r
1318         {\r
1319             if (oldChild == null)\r
1320             {\r
1321                 throw new ArgumentNullException("oldChild");\r
1322             }\r
1323 \r
1324             int index = -1;\r
1325 \r
1326             if (_childnodes != null)\r
1327             {\r
1328                 index = _childnodes[oldChild];\r
1329             }\r
1330 \r
1331             if (index == -1)\r
1332             {\r
1333                 throw new ArgumentException(HtmlDocument.HtmlExceptionRefNotChild);\r
1334             }\r
1335 \r
1336             if (_childnodes != null)\r
1337                 _childnodes.Remove(index);\r
1338 \r
1339             _ownerdocument.SetIdForNode(null, oldChild.GetId());\r
1340             _outerchanged = true;\r
1341             _innerchanged = true;\r
1342             return oldChild;\r
1343         }\r
1344 \r
1345         /// <summary>\r
1346         /// Removes the specified child node.\r
1347         /// </summary>\r
1348         /// <param name="oldChild">The node being removed. May not be <c>null</c>.</param>\r
1349         /// <param name="keepGrandChildren">true to keep grand children of the node, false otherwise.</param>\r
1350         /// <returns>The node removed.</returns>\r
1351         public HtmlNode RemoveChild(HtmlNode oldChild, bool keepGrandChildren)\r
1352         {\r
1353             if (oldChild == null)\r
1354             {\r
1355                 throw new ArgumentNullException("oldChild");\r
1356             }\r
1357 \r
1358             if ((oldChild._childnodes != null) && keepGrandChildren)\r
1359             {\r
1360                 // get prev sibling\r
1361                 HtmlNode prev = oldChild.PreviousSibling;\r
1362 \r
1363                 // reroute grand children to ourselves\r
1364                 foreach (HtmlNode grandchild in oldChild._childnodes)\r
1365                 {\r
1366                     InsertAfter(grandchild, prev);\r
1367                 }\r
1368             }\r
1369             RemoveChild(oldChild);\r
1370             _outerchanged = true;\r
1371             _innerchanged = true;\r
1372             return oldChild;\r
1373         }\r
1374 \r
1375         /// <summary>\r
1376         /// Replaces the child node oldChild with newChild node.\r
1377         /// </summary>\r
1378         /// <param name="newChild">The new node to put in the child list.</param>\r
1379         /// <param name="oldChild">The node being replaced in the list.</param>\r
1380         /// <returns>The node replaced.</returns>\r
1381         public HtmlNode ReplaceChild(HtmlNode newChild, HtmlNode oldChild)\r
1382         {\r
1383             if (newChild == null)\r
1384             {\r
1385                 return RemoveChild(oldChild);\r
1386             }\r
1387 \r
1388             if (oldChild == null)\r
1389             {\r
1390                 return AppendChild(newChild);\r
1391             }\r
1392 \r
1393             int index = -1;\r
1394 \r
1395             if (_childnodes != null)\r
1396             {\r
1397                 index = _childnodes[oldChild];\r
1398             }\r
1399 \r
1400             if (index == -1)\r
1401             {\r
1402                 throw new ArgumentException(HtmlDocument.HtmlExceptionRefNotChild);\r
1403             }\r
1404 \r
1405             if (_childnodes != null) _childnodes.Replace(index, newChild);\r
1406 \r
1407             _ownerdocument.SetIdForNode(null, oldChild.GetId());\r
1408             _ownerdocument.SetIdForNode(newChild, newChild.GetId());\r
1409             _outerchanged = true;\r
1410             _innerchanged = true;\r
1411             return newChild;\r
1412         }\r
1413 \r
1414         /// <summary>\r
1415         /// Selects a list of nodes matching the <see cref="XPath"/> expression.\r
1416         /// </summary>\r
1417         /// <param name="xpath">The XPath expression.</param>\r
1418         /// <returns>An <see cref="HtmlNodeCollection"/> containing a collection of nodes matching the <see cref="XPath"/> query, or <c>null</c> if no node matched the XPath expression.</returns>\r
1419         public HtmlNodeCollection SelectNodes(string xpath)\r
1420         {\r
1421             HtmlNodeCollection list = new HtmlNodeCollection(null);\r
1422 \r
1423             HtmlNodeNavigator nav = new HtmlNodeNavigator(_ownerdocument, this);\r
1424             XPathNodeIterator it = nav.Select(xpath);\r
1425             while (it.MoveNext())\r
1426             {\r
1427                 HtmlNodeNavigator n = (HtmlNodeNavigator) it.Current;\r
1428                 list.Add(n.CurrentNode);\r
1429             }\r
1430             if (list.Count == 0)\r
1431             {\r
1432                 return null;\r
1433             }\r
1434             return list;\r
1435         }\r
1436 \r
1437         /// <summary>\r
1438         /// Selects the first XmlNode that matches the XPath expression.\r
1439         /// </summary>\r
1440         /// <param name="xpath">The XPath expression. May not be null.</param>\r
1441         /// <returns>The first <see cref="HtmlNode"/> that matches the XPath query or a null reference if no matching node was found.</returns>\r
1442         public HtmlNode SelectSingleNode(string xpath)\r
1443         {\r
1444             if (xpath == null)\r
1445             {\r
1446                 throw new ArgumentNullException("xpath");\r
1447             }\r
1448 \r
1449             HtmlNodeNavigator nav = new HtmlNodeNavigator(_ownerdocument, this);\r
1450             XPathNodeIterator it = nav.Select(xpath);\r
1451             if (!it.MoveNext())\r
1452             {\r
1453                 return null;\r
1454             }\r
1455 \r
1456             HtmlNodeNavigator node = (HtmlNodeNavigator) it.Current;\r
1457             return node.CurrentNode;\r
1458         }\r
1459 \r
1460         /// <summary>\r
1461         /// Helper method to set the value of an attribute of this node. If the attribute is not found, it will be created automatically.\r
1462         /// </summary>\r
1463         /// <param name="name">The name of the attribute to set. May not be null.</param>\r
1464         /// <param name="value">The value for the attribute.</param>\r
1465         /// <returns>The corresponding attribute instance.</returns>\r
1466         public HtmlAttribute SetAttributeValue(string name, string value)\r
1467         {\r
1468             if (name == null)\r
1469             {\r
1470                 throw new ArgumentNullException("name");\r
1471             }\r
1472             HtmlAttribute att = Attributes[name];\r
1473             if (att == null)\r
1474             {\r
1475                 return Attributes.Append(_ownerdocument.CreateAttribute(name, value));\r
1476             }\r
1477             att.Value = value;\r
1478             return att;\r
1479         }\r
1480 \r
1481         /// <summary>\r
1482         /// Saves all the children of the node to the specified TextWriter.\r
1483         /// </summary>\r
1484         /// <param name="outText">The TextWriter to which you want to save.</param>\r
1485         public void WriteContentTo(TextWriter outText)\r
1486         {\r
1487             if (_childnodes == null)\r
1488             {\r
1489                 return;\r
1490             }\r
1491 \r
1492             foreach (HtmlNode node in _childnodes)\r
1493             {\r
1494                 node.WriteTo(outText);\r
1495             }\r
1496         }\r
1497 \r
1498         /// <summary>\r
1499         /// Saves all the children of the node to a string.\r
1500         /// </summary>\r
1501         /// <returns>The saved string.</returns>\r
1502         public string WriteContentTo()\r
1503         {\r
1504             StringWriter sw = new StringWriter();\r
1505             WriteContentTo(sw);\r
1506             sw.Flush();\r
1507             return sw.ToString();\r
1508         }\r
1509 \r
1510         /// <summary>\r
1511         /// Saves the current node to the specified TextWriter.\r
1512         /// </summary>\r
1513         /// <param name="outText">The TextWriter to which you want to save.</param>\r
1514         public void WriteTo(TextWriter outText)\r
1515         {\r
1516             string html;\r
1517             switch (_nodetype)\r
1518             {\r
1519                 case HtmlNodeType.Comment:\r
1520                     html = ((HtmlCommentNode) this).Comment;\r
1521                     if (_ownerdocument.OptionOutputAsXml)\r
1522                     {\r
1523                         outText.Write("<!--" + GetXmlComment((HtmlCommentNode) this) + " -->");\r
1524                     }\r
1525                     else\r
1526                     {\r
1527                         outText.Write(html);\r
1528                     }\r
1529                     break;\r
1530 \r
1531                 case HtmlNodeType.Document:\r
1532                     if (_ownerdocument.OptionOutputAsXml)\r
1533                     {\r
1534                         outText.Write("<?xml version=\"1.0\" encoding=\"" + _ownerdocument.GetOutEncoding().BodyName +\r
1535                                       "\"?>");\r
1536 \r
1537                         // check there is a root element\r
1538                         if (_ownerdocument.DocumentNode.HasChildNodes)\r
1539                         {\r
1540                             int rootnodes = _ownerdocument.DocumentNode._childnodes.Count;\r
1541                             if (rootnodes > 0)\r
1542                             {\r
1543                                 HtmlNode xml = _ownerdocument.GetXmlDeclaration();\r
1544                                 if (xml != null)\r
1545                                 {\r
1546                                     rootnodes --;\r
1547                                 }\r
1548 \r
1549                                 if (rootnodes > 1)\r
1550                                 {\r
1551                                     if (_ownerdocument.OptionOutputUpperCase)\r
1552                                     {\r
1553                                         outText.Write("<SPAN>");\r
1554                                         WriteContentTo(outText);\r
1555                                         outText.Write("</SPAN>");\r
1556                                     }\r
1557                                     else\r
1558                                     {\r
1559                                         outText.Write("<span>");\r
1560                                         WriteContentTo(outText);\r
1561                                         outText.Write("</span>");\r
1562                                     }\r
1563                                     break;\r
1564                                 }\r
1565                             }\r
1566                         }\r
1567                     }\r
1568                     WriteContentTo(outText);\r
1569                     break;\r
1570 \r
1571                 case HtmlNodeType.Text:\r
1572                     html = ((HtmlTextNode) this).Text;\r
1573                     if (_ownerdocument.OptionOutputAsXml)\r
1574                     {\r
1575                         outText.Write(HtmlDocument.HtmlEncode(html));\r
1576                     }\r
1577                     else\r
1578                     {\r
1579                         outText.Write(html);\r
1580                     }\r
1581                     break;\r
1582 \r
1583                 case HtmlNodeType.Element:\r
1584                     string name;\r
1585                     if (_ownerdocument.OptionOutputUpperCase)\r
1586                     {\r
1587                         name = Name.ToUpper();\r
1588                     }\r
1589                     else\r
1590                     {\r
1591                         name = Name;\r
1592                     }\r
1593 \r
1594                     if (_ownerdocument.OptionOutputOriginalCase)\r
1595                         name = OriginalName;\r
1596 \r
1597                     if (_ownerdocument.OptionOutputAsXml)\r
1598                     {\r
1599                         if (name.Length > 0)\r
1600                         {\r
1601                             if (name[0] == '?')\r
1602                             {\r
1603                                 // forget this one, it's been done at the document level\r
1604                                 break;\r
1605                             }\r
1606 \r
1607                             if (name.Trim().Length == 0)\r
1608                             {\r
1609                                 break;\r
1610                             }\r
1611                             name = HtmlDocument.GetXmlName(name);\r
1612                         }\r
1613                         else\r
1614                         {\r
1615                             break;\r
1616                         }\r
1617                     }\r
1618 \r
1619                     outText.Write("<" + name);\r
1620                     WriteAttributes(outText, false);\r
1621 \r
1622                     if (!HasChildNodes)\r
1623                     {\r
1624                         if (IsEmptyElement(Name))\r
1625                         {\r
1626                             if ((_ownerdocument.OptionWriteEmptyNodes) || (_ownerdocument.OptionOutputAsXml))\r
1627                             {\r
1628                                 outText.Write(" />");\r
1629                             }\r
1630                             else\r
1631                             {\r
1632                                 if (Name.Length > 0)\r
1633                                 {\r
1634                                     if (Name[0] == '?')\r
1635                                     {\r
1636                                         outText.Write("?");\r
1637                                     }\r
1638                                 }\r
1639 \r
1640                                 outText.Write(">");\r
1641                             }\r
1642                         }\r
1643                         else\r
1644                         {\r
1645                             outText.Write("></" + name + ">");\r
1646                         }\r
1647                     }\r
1648                     else\r
1649                     {\r
1650                         outText.Write(">");\r
1651                         bool cdata = false;\r
1652                         if (_ownerdocument.OptionOutputAsXml)\r
1653                         {\r
1654                             if (IsCDataElement(Name))\r
1655                             {\r
1656                                 // this code and the following tries to output things as nicely as possible for old browsers.\r
1657                                 cdata = true;\r
1658                                 outText.Write("\r\n//<![CDATA[\r\n");\r
1659                             }\r
1660                         }\r
1661 \r
1662                         if (cdata)\r
1663                         {\r
1664                             if (HasChildNodes)\r
1665                             {\r
1666                                 // child must be a text\r
1667                                 ChildNodes[0].WriteTo(outText);\r
1668                             }\r
1669                             outText.Write("\r\n//]]>//\r\n");\r
1670                         }\r
1671                         else\r
1672                         {\r
1673                             WriteContentTo(outText);\r
1674                         }\r
1675 \r
1676                         outText.Write("</" + name);\r
1677                         if (!_ownerdocument.OptionOutputAsXml)\r
1678                         {\r
1679                             WriteAttributes(outText, true);\r
1680                         }\r
1681                         outText.Write(">");\r
1682                     }\r
1683                     break;\r
1684             }\r
1685         }\r
1686 \r
1687         /// <summary>\r
1688         /// Saves the current node to the specified XmlWriter.\r
1689         /// </summary>\r
1690         /// <param name="writer">The XmlWriter to which you want to save.</param>\r
1691         public void WriteTo(XmlWriter writer)\r
1692         {\r
1693             switch (_nodetype)\r
1694             {\r
1695                 case HtmlNodeType.Comment:\r
1696                     writer.WriteComment(GetXmlComment((HtmlCommentNode) this));\r
1697                     break;\r
1698 \r
1699                 case HtmlNodeType.Document:\r
1700                     writer.WriteProcessingInstruction("xml",\r
1701                                                       "version=\"1.0\" encoding=\"" +\r
1702                                                       _ownerdocument.GetOutEncoding().BodyName + "\"");\r
1703                     if (HasChildNodes)\r
1704                     {\r
1705                         foreach (HtmlNode subnode in ChildNodes)\r
1706                         {\r
1707                             subnode.WriteTo(writer);\r
1708                         }\r
1709                     }\r
1710                     break;\r
1711 \r
1712                 case HtmlNodeType.Text:\r
1713                     string html = ((HtmlTextNode) this).Text;\r
1714                     writer.WriteString(html);\r
1715                     break;\r
1716 \r
1717                 case HtmlNodeType.Element:\r
1718                     string name = _ownerdocument.OptionOutputUpperCase ? Name.ToUpper() : Name;\r
1719 \r
1720                     if (_ownerdocument.OptionOutputOriginalCase)\r
1721                         name = OriginalName;\r
1722 \r
1723                     writer.WriteStartElement(name);\r
1724                     WriteAttributes(writer, this);\r
1725 \r
1726                     if (HasChildNodes)\r
1727                     {\r
1728                         foreach (HtmlNode subnode in ChildNodes)\r
1729                         {\r
1730                             subnode.WriteTo(writer);\r
1731                         }\r
1732                     }\r
1733                     writer.WriteEndElement();\r
1734                     break;\r
1735             }\r
1736         }\r
1737 \r
1738         /// <summary>\r
1739         /// Saves the current node to a string.\r
1740         /// </summary>\r
1741         /// <returns>The saved string.</returns>\r
1742         public string WriteTo()\r
1743         {\r
1744             using (StringWriter sw = new StringWriter())\r
1745             {\r
1746                 WriteTo(sw);\r
1747                 sw.Flush();\r
1748                 return sw.ToString();\r
1749             }\r
1750         }\r
1751 \r
1752         #endregion\r
1753 \r
1754         #region Internal Methods\r
1755 \r
1756         internal static string GetXmlComment(HtmlCommentNode comment)\r
1757         {\r
1758             string s = comment.Comment;\r
1759             return s.Substring(4, s.Length - 7).Replace("--", " - -");\r
1760         }\r
1761 \r
1762         internal static void WriteAttributes(XmlWriter writer, HtmlNode node)\r
1763         {\r
1764             if (!node.HasAttributes)\r
1765             {\r
1766                 return;\r
1767             }\r
1768             // we use Hashitems to make sure attributes are written only once\r
1769             foreach (HtmlAttribute att in node.Attributes.Hashitems.Values)\r
1770             {\r
1771                 writer.WriteAttributeString(att.XmlName, att.Value);\r
1772             }\r
1773         }\r
1774 \r
1775         internal void CloseNode(HtmlNode endnode)\r
1776         {\r
1777             if (!_ownerdocument.OptionAutoCloseOnEnd)\r
1778             {\r
1779                 // close all children\r
1780                 if (_childnodes != null)\r
1781                 {\r
1782                     foreach (HtmlNode child in _childnodes)\r
1783                     {\r
1784                         if (child.Closed)\r
1785                             continue;\r
1786 \r
1787                         // create a fake closer node\r
1788                         HtmlNode close = new HtmlNode(NodeType, _ownerdocument, -1);\r
1789                         close._endnode = close;\r
1790                         child.CloseNode(close);\r
1791                     }\r
1792                 }\r
1793             }\r
1794 \r
1795             if (!Closed)\r
1796             {\r
1797                 _endnode = endnode;\r
1798 \r
1799                 if (_ownerdocument._openednodes != null)\r
1800                 {\r
1801                     _ownerdocument._openednodes.Remove(_outerstartindex);\r
1802                 }\r
1803 \r
1804                 HtmlNode self = _ownerdocument._lastnodes[Name] as HtmlNode;\r
1805                 if (self == this)\r
1806                 {\r
1807                     _ownerdocument._lastnodes.Remove(Name);\r
1808                     _ownerdocument.UpdateLastParentNode();\r
1809                 }\r
1810 \r
1811                 if (endnode == this)\r
1812                     return;\r
1813 \r
1814                 // create an inner section\r
1815                 _innerstartindex = _outerstartindex + _outerlength;\r
1816                 _innerlength = endnode._outerstartindex - _innerstartindex;\r
1817 \r
1818                 // update full length\r
1819                 _outerlength = (endnode._outerstartindex + endnode._outerlength) - _outerstartindex;\r
1820             }\r
1821         }\r
1822 \r
1823         internal string GetId()\r
1824         {\r
1825             HtmlAttribute att = Attributes["id"];\r
1826             if (att == null)\r
1827             {\r
1828                 return null;\r
1829             }\r
1830             return att.Value;\r
1831         }\r
1832 \r
1833         internal void SetId(string id)\r
1834         {\r
1835             HtmlAttribute att = Attributes["id"];\r
1836             if (att == null)\r
1837             {\r
1838                 att = _ownerdocument.CreateAttribute("id");\r
1839             }\r
1840             att.Value = id;\r
1841             _ownerdocument.SetIdForNode(this, att.Value);\r
1842             _outerchanged = true;\r
1843         }\r
1844 \r
1845         internal void WriteAttribute(TextWriter outText, HtmlAttribute att)\r
1846         {\r
1847             string name;\r
1848             string quote = att.QuoteType == AttributeValueQuote.DoubleQuote ? "\"" : "'";\r
1849             if (_ownerdocument.OptionOutputAsXml)\r
1850             {\r
1851                 if (_ownerdocument.OptionOutputUpperCase)\r
1852                 {\r
1853                     name = att.XmlName.ToUpper();\r
1854                 }\r
1855                 else\r
1856                 {\r
1857                     name = att.XmlName;\r
1858                 }\r
1859                 if (_ownerdocument.OptionOutputOriginalCase)\r
1860                     name = att.OriginalName;\r
1861 \r
1862                 outText.Write(" " + name + "=" + quote + HtmlDocument.HtmlEncode(att.XmlValue) + quote);\r
1863             }\r
1864             else\r
1865             {\r
1866                 if (_ownerdocument.OptionOutputUpperCase)\r
1867                 {\r
1868                     name = att.Name.ToUpper();\r
1869                 }\r
1870                 else\r
1871                 {\r
1872                     name = att.Name;\r
1873                 }\r
1874 \r
1875                 if (att.Name.Length >= 4)\r
1876                 {\r
1877                     if ((att.Name[0] == '<') && (att.Name[1] == '%') &&\r
1878                         (att.Name[att.Name.Length - 1] == '>') && (att.Name[att.Name.Length - 2] == '%'))\r
1879                     {\r
1880                         outText.Write(" " + name);\r
1881                         return;\r
1882                     }\r
1883                 }\r
1884                 if (_ownerdocument.OptionOutputOptimizeAttributeValues)\r
1885                 {\r
1886                     if (att.Value.IndexOfAny(new Char[] {(char) 10, (char) 13, (char) 9, ' '}) < 0)\r
1887                     {\r
1888                         outText.Write(" " + name + "=" + att.Value);\r
1889                     }\r
1890                     else\r
1891                     {\r
1892                         outText.Write(" " + name + "=" + quote + att.Value + quote);\r
1893                     }\r
1894                 }\r
1895                 else\r
1896                 {\r
1897                     outText.Write(" " + name + "=" + quote + att.Value + quote);\r
1898                 }\r
1899             }\r
1900         }\r
1901 \r
1902         internal void WriteAttributes(TextWriter outText, bool closing)\r
1903         {\r
1904             if (_ownerdocument.OptionOutputAsXml)\r
1905             {\r
1906                 if (_attributes == null)\r
1907                 {\r
1908                     return;\r
1909                 }\r
1910                 // we use Hashitems to make sure attributes are written only once\r
1911                 foreach (HtmlAttribute att in _attributes.Hashitems.Values)\r
1912                 {\r
1913                     WriteAttribute(outText, att);\r
1914                 }\r
1915                 return;\r
1916             }\r
1917 \r
1918             if (!closing)\r
1919             {\r
1920                 if (_attributes != null)\r
1921                 {\r
1922                     foreach (HtmlAttribute att in _attributes)\r
1923                     {\r
1924                         WriteAttribute(outText, att);\r
1925                     }\r
1926                 }\r
1927                 if (_ownerdocument.OptionAddDebuggingAttributes)\r
1928                 {\r
1929                     WriteAttribute(outText, _ownerdocument.CreateAttribute("_closed", Closed.ToString()));\r
1930                     WriteAttribute(outText, _ownerdocument.CreateAttribute("_children", ChildNodes.Count.ToString()));\r
1931 \r
1932                     int i = 0;\r
1933                     foreach (HtmlNode n in ChildNodes)\r
1934                     {\r
1935                         WriteAttribute(outText, _ownerdocument.CreateAttribute("_child_" + i,\r
1936                                                                                n.Name));\r
1937                         i++;\r
1938                     }\r
1939                 }\r
1940             }\r
1941             else\r
1942             {\r
1943                 if (_endnode == null)\r
1944                 {\r
1945                     return;\r
1946                 }\r
1947 \r
1948                 if (_endnode._attributes == null)\r
1949                 {\r
1950                     return;\r
1951                 }\r
1952 \r
1953                 if (_endnode == this)\r
1954                 {\r
1955                     return;\r
1956                 }\r
1957 \r
1958                 foreach (HtmlAttribute att in _endnode._attributes)\r
1959                 {\r
1960                     WriteAttribute(outText, att);\r
1961                 }\r
1962                 if (_ownerdocument.OptionAddDebuggingAttributes)\r
1963                 {\r
1964                     WriteAttribute(outText, _ownerdocument.CreateAttribute("_closed", Closed.ToString()));\r
1965                     WriteAttribute(outText, _ownerdocument.CreateAttribute("_children", ChildNodes.Count.ToString()));\r
1966                 }\r
1967             }\r
1968         }\r
1969 \r
1970         #endregion\r
1971 \r
1972         #region Private Methods\r
1973 \r
1974         private string GetRelativeXpath()\r
1975         {\r
1976             if (ParentNode == null)\r
1977                 return Name;\r
1978             if (NodeType == HtmlNodeType.Document)\r
1979                 return string.Empty;\r
1980 \r
1981             int i = 1;\r
1982             foreach (HtmlNode node in ParentNode.ChildNodes)\r
1983             {\r
1984                 if (node.Name != Name) continue;\r
1985 \r
1986                 if (node == this)\r
1987                     break;\r
1988 \r
1989                 i++;\r
1990             }\r
1991             return Name + "[" + i + "]";\r
1992         }\r
1993 \r
1994         #endregion\r
1995     }\r
1996 }