Merge pull request #347 from JamesB7/master
[mono.git] / mcs / class / System.Xml.Linq / System.Xml.Linq / XNode.cs
1 //
2 // Authors:
3 //   Atsushi Enomoto
4 //
5 // Copyright 2007 Novell (http://www.novell.com)
6 //
7 // Permission is hereby granted, free of charge, to any person obtaining
8 // a copy of this software and associated documentation files (the
9 // "Software"), to deal in the Software without restriction, including
10 // without limitation the rights to use, copy, modify, merge, publish,
11 // distribute, sublicense, and/or sell copies of the Software, and to
12 // permit persons to whom the Software is furnished to do so, subject to
13 // the following conditions:
14 // 
15 // The above copyright notice and this permission notice shall be
16 // included in all copies or substantial portions of the Software.
17 // 
18 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
19 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
20 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
21 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
22 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
23 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
24 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
25 //
26
27 using System;
28 using System.Collections;
29 using System.Collections.Generic;
30 using System.IO;
31 using System.Text;
32 using System.Xml;
33
34 using XPI = System.Xml.Linq.XProcessingInstruction;
35
36 namespace System.Xml.Linq
37 {
38         public abstract class XNode : XObject
39         {
40                 public static int CompareDocumentOrder (XNode n1, XNode n2)
41                 {
42                         return order_comparer.Compare (n1, n2);
43                 }
44
45                 public static bool DeepEquals (XNode n1, XNode n2)
46                 {
47                         return eq_comparer.Equals (n1, n2);
48                 }
49
50                 static XNodeEqualityComparer eq_comparer =
51                         new XNodeEqualityComparer ();
52                 static XNodeDocumentOrderComparer order_comparer =
53                         new XNodeDocumentOrderComparer ();
54
55                 XNode previous;
56                 XNode next;
57
58                 internal XNode ()
59                 {
60                 }
61
62                 public static XNodeDocumentOrderComparer DocumentOrderComparer {
63                         get { return order_comparer; }
64                 }
65
66                 public static XNodeEqualityComparer EqualityComparer {
67                         get { return eq_comparer; }
68                 }
69
70                 public XNode PreviousNode {
71                         get { return previous; }
72                         internal set { previous = value; }
73                 }
74
75                 public XNode NextNode {
76                         get { return next; }
77                         internal set { next = value; }
78                 }
79
80                 public string ToString (SaveOptions options)
81                 {
82                         StringWriter sw = new StringWriter ();
83                         XmlWriterSettings s = new XmlWriterSettings ();
84                         s.ConformanceLevel = ConformanceLevel.Auto;
85                         s.Indent = options != SaveOptions.DisableFormatting;
86                         XmlWriter xw = XmlWriter.Create (sw, s);
87                         WriteTo (xw);
88                         xw.Close ();
89                         return sw.ToString ();
90                 }
91
92                 public void AddAfterSelf (object content)
93                 {
94                         if (Owner == null)
95                                 throw new InvalidOperationException ();
96                         XNode here = this;
97                         XNode orgNext = next;
98                         foreach (object o in XUtil.ExpandArray (content)) {
99                                 if (o == null || Owner.OnAddingObject (o, true, here, false))
100                                         continue;
101                                 XNode n = XUtil.ToNode (o);
102                                 n = (XNode) XUtil.GetDetachedObject (n);
103                                 n.SetOwner (Owner);
104                                 n.previous = here;
105                                 here.next = n;
106                                 n.next = orgNext;
107                                 if (orgNext != null)
108                                         orgNext.previous = n;
109                                 else
110                                         Owner.LastNode = n;
111                                 here = n;
112                         }
113                 }
114
115                 public void AddAfterSelf (params object [] content)
116                 {
117                         if (Owner == null)
118                                 throw new InvalidOperationException ();
119                         AddAfterSelf ((object) content);
120                 }
121
122                 public void AddBeforeSelf (object content)
123                 {
124                         if (Owner == null)
125                                 throw new InvalidOperationException ();
126                         foreach (object o in XUtil.ExpandArray (content)) {
127                                 if (o == null || Owner.OnAddingObject (o, true, previous, true))
128                                         continue;
129                                 XNode n = XUtil.ToNode (o);
130                                 n = (XNode) XUtil.GetDetachedObject (n);
131                                 n.SetOwner (Owner);
132                                 n.previous = previous;
133                                 n.next = this;
134                                 if (previous != null)
135                                         previous.next = n;
136                                 previous = n;
137                                 if (Owner.FirstNode == this)
138                                         Owner.FirstNode = n;
139                         }
140                 }
141
142                 public void AddBeforeSelf (params object [] content)
143                 {
144                         if (Owner == null)
145                                 throw new InvalidOperationException ();
146                         AddBeforeSelf ((object) content);
147                 }
148
149                 public static XNode ReadFrom (XmlReader reader)
150                 {
151                         return ReadFrom (reader, LoadOptions.None);
152                 }
153
154                 internal static XNode ReadFrom (XmlReader r, LoadOptions options)
155                 {
156                         switch (r.NodeType) {
157                         case XmlNodeType.Element:
158                                 return XElement.LoadCore (r, options);
159                         case XmlNodeType.Whitespace:
160                         case XmlNodeType.SignificantWhitespace:
161                         case XmlNodeType.Text:
162                                 XText t = new XText (r.Value);
163                                 t.FillLineInfoAndBaseUri (r, options);
164                                 r.Read ();
165                                 return t;
166                         case XmlNodeType.CDATA:
167                                 XCData c = new XCData (r.Value);
168                                 c.FillLineInfoAndBaseUri (r, options);
169                                 r.Read ();
170                                 return c;
171                         case XmlNodeType.ProcessingInstruction:
172                                 XPI pi = new XPI (r.Name, r.Value);
173                                 pi.FillLineInfoAndBaseUri (r, options);
174                                 r.Read ();
175                                 return pi;
176                         case XmlNodeType.Comment:
177                                 XComment cm = new XComment (r.Value);
178                                 cm.FillLineInfoAndBaseUri (r, options);
179                                 r.Read ();
180                                 return cm;
181                         case XmlNodeType.DocumentType:
182                                 XDocumentType d = new XDocumentType (r.Name,
183                                         r.GetAttribute ("PUBLIC"),
184                                         r.GetAttribute ("SYSTEM"),
185                                         r.Value);
186                                 d.FillLineInfoAndBaseUri (r, options);
187                                 r.Read ();
188                                 return d;
189                         default:
190                                 throw new InvalidOperationException (String.Format ("Node type {0} is not supported", r.NodeType));
191                         }
192                 }
193
194                 public void Remove ()
195                 {
196                         if (Owner == null)
197                                 throw new InvalidOperationException ("Owner is missing");
198
199                         if (Owner.FirstNode == this)
200                                 Owner.FirstNode = next;
201                         if (Owner.LastNode == this)
202                                 Owner.LastNode = previous;
203                         if (previous != null)
204                                 previous.next = next;
205                         if (next != null)
206                                 next.previous = previous;
207                         previous = null;
208                         next = null;
209                         SetOwner (null);
210                 }
211
212                 public override string ToString ()
213                 {
214                         return ToString (SaveOptions.None);
215                 }
216
217                 public abstract void WriteTo (XmlWriter writer);
218
219                 public IEnumerable<XElement> Ancestors ()
220                 {
221                         for (XElement el = Parent; el != null; el = el.Parent)
222                                 yield return el;
223                 }
224
225                 public IEnumerable<XElement> Ancestors (XName name)
226                 {
227                         foreach (XElement el in Ancestors ())
228                                 if (el.Name == name)
229                                         yield return el;
230                 }
231
232                 public XmlReader CreateReader ()
233                 {
234                         return new XNodeReader (this);
235                 }
236
237 #if NET_4_0
238                 public XmlReader CreateReader (ReaderOptions readerOptions)
239                 {
240                         var r = new XNodeReader (this);
241                         if ((readerOptions & ReaderOptions.OmitDuplicateNamespaces) != 0)
242                                 r.OmitDuplicateNamespaces = true;
243                         
244                         return r;
245                 }
246 #endif
247
248                 public IEnumerable<XElement> ElementsAfterSelf ()
249                 {
250                         foreach (XNode n in NodesAfterSelf ())
251                                 if (n is XElement)
252                                         yield return (XElement) n;
253                 }
254
255                 public IEnumerable<XElement> ElementsAfterSelf (XName name)
256                 {
257                         foreach (XElement el in ElementsAfterSelf ())
258                                 if (el.Name == name)
259                                         yield return el;
260                 }
261
262                 public IEnumerable<XElement> ElementsBeforeSelf ()
263                 {
264                         foreach (XNode n in NodesBeforeSelf ())
265                                 if (n is XElement)
266                                         yield return (XElement) n;
267                 }
268
269                 public IEnumerable<XElement> ElementsBeforeSelf (XName name)
270                 {
271                         foreach (XElement el in ElementsBeforeSelf ())
272                                 if (el.Name == name)
273                                         yield return el;
274                 }
275
276                 public bool IsAfter (XNode node)
277                 {
278                         return XNode.DocumentOrderComparer.Compare (this, node) > 0;
279                 }
280
281                 public bool IsBefore (XNode node)
282                 {
283                         return XNode.DocumentOrderComparer.Compare (this, node) < 0;
284                 }
285
286                 public IEnumerable<XNode> NodesAfterSelf ()
287                 {
288                         if (Owner == null)
289                                 yield break;
290                         for (XNode n = NextNode; n != null; n = n.NextNode)
291                                 yield return n;
292                 }
293
294                 public IEnumerable<XNode> NodesBeforeSelf ()
295                 {
296                         if (Owner == null)
297                                 yield break;
298                         for (XNode n = Owner.FirstNode; n != this; n = n.NextNode)
299                                 yield return n;
300                 }
301
302                 public void ReplaceWith (object content)
303                 {
304                         if (Owner == null)
305                                 throw new InvalidOperationException ();
306
307                         XNode here = previous;
308                         XNode orgNext = next;
309                         XContainer orgOwner = Owner;
310                         Remove();
311                         foreach (object o in XUtil.ExpandArray (content)) {
312                                 if (o == null || orgOwner.OnAddingObject (o, true, here, false))
313                                         continue;
314                                 XNode n = XUtil.ToNode (o);
315                                 n = (XNode) XUtil.GetDetachedObject (n);
316                                 n.SetOwner (orgOwner);
317                                 n.previous = here;
318                                 if (here != null)
319                                         here.next = n;
320                                 else
321                                         orgOwner.FirstNode = n;
322                                 n.next = orgNext;
323                                 if (orgNext != null)
324                                         orgNext.previous = n;
325                                 else
326                                         orgOwner.LastNode = n;
327                                 here = n;
328                         }
329                 }
330
331                 public void ReplaceWith (params object [] content)
332                 {
333                         if (Owner == null)
334                                 throw new InvalidOperationException ();
335                         ReplaceWith ((object) content);
336                 }
337         }
338 }