Merge pull request #799 from kebby/master
[mono.git] / mcs / class / System.Xml.Linq / System.Xml.Linq / XContainer.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 namespace System.Xml.Linq
35 {
36         public abstract class XContainer : XNode
37         {
38                 internal XContainer ()
39                 {
40                 }
41
42                 XNode first;
43                 XNode last;
44
45                 public XNode FirstNode {
46                         get { return first; }
47                         internal set { first = value; }
48                 }
49
50                 public XNode LastNode {
51                         get { return last; }
52                         internal set { last = value; }
53                 }
54
55                 void CheckChildType (object o, bool addFirst)
56                 {
57                         if (o == null || o is string || o is XNode)
58                                 return;
59                         if (o is IEnumerable) {
60                                 foreach (object oc in ((IEnumerable) o))
61                                         CheckChildType (oc, addFirst);
62                                 return;
63                         }
64                         else
65                                 throw new ArgumentException ("Invalid child type: " + o.GetType ());
66                 }
67
68                 public void Add (object content)
69                 {
70                         if (content == null)
71                                 return;
72
73                         foreach (object o in XUtil.ExpandArray (content))
74                         {
75                                 if (!OnAddingObject (o, false, last, false))
76                                 {
77                                         var node = XUtil.ToNode (o);
78                                         OnAddingObject (node);
79                                         AddNode (node);
80                                         OnAddedObject (node);
81                                 }
82                         }
83                 }
84                 
85                 void AddNode (XNode n)
86                 {
87                         CheckChildType (n, false);
88                         n = (XNode) XUtil.GetDetachedObject (n);
89                         n.SetOwner (this);
90                         if (first == null)
91                                 last = first = n;
92                         else {
93                                 last.NextNode = n;
94                                 n.PreviousNode = last;
95                                 last = n;
96                         }
97                 }
98
99                 public void Add (params object [] content)
100                 {
101                         if (content == null)
102                                 return;
103                         foreach (object o in XUtil.ExpandArray (content))
104                                 Add (o);
105                 }
106
107                 public void AddFirst (object content)
108                 {
109                         if (first == null)
110                                 Add (content);
111                         else
112                                 first.AddBeforeSelf (XUtil.ExpandArray (content));
113                 }
114
115                 public void AddFirst (params object [] content)
116                 {
117                         if (content == null)
118                                 return;
119                         if (first == null)
120                                 Add (content);
121                         else
122                                 foreach (object o in XUtil.ExpandArray (content))
123                                         if (!OnAddingObject (o, false, first.PreviousNode, true))
124                                                 first.AddBeforeSelf (o);
125                 }
126
127                 internal virtual bool OnAddingObject (object o, bool rejectAttribute, XNode refNode, bool addFirst)
128                 {
129                         return false;
130                 }
131                 
132                 public XmlWriter CreateWriter ()
133                 {
134                         return new XNodeWriter (this);
135                 }
136
137                 public IEnumerable <XNode> Nodes ()
138                 {
139                         XNode next;
140                         for (XNode n = FirstNode; n != null; n = next) {
141                                 next = n.NextNode;
142                                 yield return n;
143                         }
144                 }
145
146                 public IEnumerable<XNode> DescendantNodes ()
147                 {
148                         foreach (XNode n in Nodes ()) {
149                                 yield return n;
150                                 XContainer c = n as XContainer;
151                                 if (c != null)
152                                         foreach (XNode d in c.DescendantNodes ())
153                                                 yield return d;
154                         }
155                 }
156
157                 public IEnumerable <XElement> Descendants ()
158                 {
159                         foreach (XNode n in DescendantNodes ()) {
160                                 XElement el = n as XElement;
161                                 if (el != null)
162                                         yield return el;
163                         }
164                 }
165
166                 public IEnumerable <XElement> Descendants (XName name)
167                 {
168                         foreach (XElement el in Descendants ())
169                                 if (el.Name == name)
170                                         yield return el;
171                 }
172
173                 public IEnumerable <XElement> Elements ()
174                 {
175                         foreach (XNode n in Nodes ()) {
176                                 XElement el = n as XElement;
177                                 if (el != null)
178                                         yield return el;
179                         }
180                 }
181
182                 public IEnumerable <XElement> Elements (XName name)
183                 {
184                         foreach (XNode n in Nodes ()) {
185                                 XElement el = n as XElement;
186                                 if (el != null && el.Name == name)
187                                         yield return el;
188                         }
189                 }
190
191                 public XElement Element (XName name)
192                 {
193                         foreach (XNode n in Nodes ()) {
194                                 XElement el = n as XElement;
195                                 if (el != null && el.Name == name)
196                                         return el;
197                         }
198                         return null;
199                 }
200
201                 internal void ReadContentFrom (XmlReader reader, LoadOptions options)
202                 {
203                         while (!reader.EOF) {
204                                 if (reader.NodeType == XmlNodeType.EndElement)
205                                         // end of the element.
206                                         break;
207                                 Add (XNode.ReadFrom (reader, options));
208                         }
209                 }
210
211                 public void RemoveNodes ()
212                 {
213                         foreach (XNode n in Nodes ())
214                                 n.Remove ();
215                 }
216
217                 public void ReplaceNodes (object content)
218                 {
219                         // First, it creates a snapshot copy, then removes the contents, and then adds the copy. http://msdn.microsoft.com/en-us/library/system.xml.linq.xcontainer.replacenodes.aspx
220
221                         if (FirstNode == null) {
222                                 Add (content);
223                                 return;
224                         }
225
226                         var l = new List<object> ();
227                         foreach (var obj in XUtil.ExpandArray (content))
228                                 l.Add (obj);
229
230                         RemoveNodes ();
231                         Add (l);
232                 }
233
234                 public void ReplaceNodes (params object [] content)
235                 {
236                         ReplaceNodes ((object) content);
237                 }
238         }
239 }