2255eae5bf18025d48188bb7eceed63046e5dca1
[mono.git] / mcs / class / System.Xml.Linq / System.Xml.Linq / XDocument.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 using System.Xml.Schema;
34 using System.Xml.Serialization;
35
36 namespace System.Xml.Linq
37 {
38         public class XDocument : XContainer
39         {
40                 XDeclaration xmldecl;
41
42                 public XDocument ()
43                 {
44                 }
45
46                 public XDocument (params object [] content)
47                 {
48                         Add (content);
49                 }
50
51                 public XDocument (XDeclaration xmldecl, params object [] content)
52                 {
53                         Declaration = xmldecl;
54                         Add (content);
55                 }
56
57                 public XDocument (XDocument other)
58                 {
59                         foreach (object o in other.Nodes ())
60                                 Add (XUtil.Clone (o));
61                 }
62
63                 public XDeclaration Declaration {
64                         get { return xmldecl; }
65                         set { xmldecl = value; }
66                 }
67
68                 public XDocumentType DocumentType {
69                         get {
70                                 foreach (object o in Nodes ())
71                                         if (o is XDocumentType)
72                                                 return (XDocumentType) o;
73                                 return null;
74                         }
75                 }
76
77                 public override XmlNodeType NodeType {
78                         get { return XmlNodeType.Document; }
79                 }
80
81                 public XElement Root {
82                         get {
83                                 foreach (object o in Nodes ())
84                                         if (o is XElement)
85                                                 return (XElement) o;
86                                 return null;
87                         }
88                 }
89
90                 public static XDocument Load (string uri)
91                 {
92                         return Load (uri, LoadOptions.None);
93                 }
94
95                 public static XDocument Load (string uri, LoadOptions options)
96                 {
97                         XmlReaderSettings s = new XmlReaderSettings ();
98                         s.IgnoreWhitespace = (options & LoadOptions.PreserveWhitespace) == 0;
99                         using (XmlReader r = XmlReader.Create (uri, s)) {
100                                 return LoadCore (r, options);
101                         }
102                 }
103
104                 public static XDocument Load (Stream stream)
105                 {
106                         return Load (new StreamReader (stream), LoadOptions.None);
107                 }
108
109                 public static XDocument Load (Stream stream, LoadOptions options)
110                 {
111                         return Load (new StreamReader (stream), options);
112                 }
113
114                 public static XDocument Load (TextReader reader)
115                 {
116                         return Load (reader, LoadOptions.None);
117                 }
118
119                 public static XDocument Load (TextReader reader, LoadOptions options)
120                 {
121                         XmlReaderSettings s = new XmlReaderSettings ();
122                         s.ProhibitDtd = false; // see XNodeNavigatorTest.MoveToId().
123                         s.IgnoreWhitespace = (options & LoadOptions.PreserveWhitespace) == 0;
124                         using (XmlReader r = XmlReader.Create (reader, s)) {
125                                 return LoadCore (r, options);
126                         }
127                 }
128
129                 public static XDocument Load (XmlReader reader)
130                 {
131                         return Load (reader, LoadOptions.None);
132                 }
133
134                 public static XDocument Load (XmlReader reader, LoadOptions options)
135                 {
136                         XmlReaderSettings s = reader.Settings != null ? reader.Settings.Clone () : new XmlReaderSettings ();
137                         s.IgnoreWhitespace = (options & LoadOptions.PreserveWhitespace) == 0;
138                         using (XmlReader r = XmlReader.Create (reader, s)) {
139                                 return LoadCore (r, options);
140                         }
141                 }
142
143                 static XDocument LoadCore (XmlReader reader, LoadOptions options)
144                 {
145                         XDocument doc = new XDocument ();
146                         doc.ReadContent (reader, options);
147                         return doc;
148                 }
149
150                 void ReadContent (XmlReader reader, LoadOptions options)
151                 {
152                         if (reader.ReadState == ReadState.Initial)
153                                 reader.Read ();
154                         if (reader.NodeType == XmlNodeType.XmlDeclaration) {
155                                 Declaration = new XDeclaration (
156                                         reader.GetAttribute ("version"),
157                                         reader.GetAttribute ("encoding"),
158                                         reader.GetAttribute ("standalone"));
159                                 reader.Read ();
160                         }
161                         ReadContentFrom (reader, options);
162                         if (Root == null)
163                                 throw new InvalidOperationException ("The document element is missing.");
164                 }
165
166                 static void ValidateWhitespace (string s)
167                 {
168                         for (int i = 0; i < s.Length; i++)
169                                 switch (s [i]) {
170                                 case ' ': case '\t': case '\n': case '\r':
171                                         continue;
172                                 default:
173                                         throw new ArgumentException ("Non-whitespace text appears directly in the document.");
174                                 }
175                 }
176
177                 public static XDocument Parse (string s)
178                 {
179                         return Parse (s, LoadOptions.None);
180                 }
181
182                 public static XDocument Parse (string s, LoadOptions options)
183                 {
184                         return Load (new StringReader (s), options);
185                 }
186
187                 public void Save (string filename)
188                 {
189                         Save (filename, SaveOptions.None);
190                 }
191
192                 public void Save (string filename, SaveOptions options)
193                 {
194                         XmlWriterSettings s = new XmlWriterSettings ();
195                         if ((options & SaveOptions.DisableFormatting) == SaveOptions.None)
196                                 s.Indent = true;
197 #if NET_4_0
198                         if ((options & SaveOptions.OmitDuplicateNamespaces) == SaveOptions.OmitDuplicateNamespaces)
199                                 s.NamespaceHandling |= NamespaceHandling.OmitDuplicates;
200 #endif
201                         
202                         using (XmlWriter w = XmlWriter.Create (filename, s)) {
203                                 Save (w);
204                         }
205                 }
206
207                 public void Save (TextWriter tw)
208                 {
209                         Save (tw, SaveOptions.None);
210                 }
211
212                 public void Save (TextWriter tw, SaveOptions options)
213                 {
214                         XmlWriterSettings s = new XmlWriterSettings ();
215                         if ((options & SaveOptions.DisableFormatting) == SaveOptions.None)
216                                 s.Indent = true;
217 #if NET_4_0
218                         if ((options & SaveOptions.OmitDuplicateNamespaces) == SaveOptions.OmitDuplicateNamespaces)
219                                 s.NamespaceHandling |= NamespaceHandling.OmitDuplicates;
220 #endif
221                         using (XmlWriter w = XmlWriter.Create (tw, s)) {
222                                 Save (w);
223                         }
224                 }
225
226                 public void Save (XmlWriter w)
227                 {
228                         WriteTo (w);
229                 }
230
231                 public override void WriteTo (XmlWriter w)
232                 {
233                         if (xmldecl != null) {
234                                 if (xmldecl.Standalone != null)
235                                         w.WriteStartDocument (xmldecl.Standalone == "yes");
236                                 else
237                                         w.WriteStartDocument ();
238                         }
239                         foreach (XNode node in Nodes ())
240                                 node.WriteTo (w);
241                 }
242
243                 internal override bool OnAddingObject (object obj, bool rejectAttribute, XNode refNode, bool addFirst)
244                 {
245                         VerifyAddedNode (obj, addFirst);
246                         return false;
247                 }
248
249                 void VerifyAddedNode (object node, bool addFirst)
250                 {
251                         if (node == null)
252                                 throw new InvalidOperationException ("Only a node is allowed here");
253
254                         if (node is string)
255                                 ValidateWhitespace ((string) node);
256                         if (node is XText)
257                                 ValidateWhitespace (((XText) node).Value);
258                         else if (node is XDocumentType) {
259                                 if (DocumentType != null)
260                                         throw new InvalidOperationException ("There already is another document type declaration");
261                                 if (Root != null && !addFirst)
262                                         throw new InvalidOperationException ("A document type cannot be added after the document element");
263                         }
264                         else if (node is XElement) {
265                                 if (Root != null)
266                                         throw new InvalidOperationException ("There already is another document element");
267                                 if (DocumentType != null && addFirst)
268                                         throw new InvalidOperationException ("An element cannot be added before the document type declaration");
269                         }
270                 }
271 #if NET_4_0
272                 public void Save (Stream stream)
273                 {
274                         Save (stream, SaveOptions.None);
275                 }
276
277                 public void Save (Stream stream, SaveOptions options)
278                 {
279                         XmlWriterSettings s = new XmlWriterSettings ();
280                         if ((options & SaveOptions.DisableFormatting) == SaveOptions.None)
281                                 s.Indent = true;
282                         if ((options & SaveOptions.OmitDuplicateNamespaces) == SaveOptions.OmitDuplicateNamespaces)
283                                 s.NamespaceHandling |= NamespaceHandling.OmitDuplicates;
284         
285                         using (var writer = XmlWriter.Create (stream, s)){
286                                 Save (writer);
287                         }
288                 }
289
290 #endif
291         }
292 }