Implement MachineKey.Protect and MachineKey.Unprotect
[mono.git] / mcs / class / System.Xml.Linq / System.Xml.Linq / XNodeWriter.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.Xml;
29
30 namespace System.Xml.Linq
31 {
32         internal class XNodeWriter : XmlWriter
33         {
34                 public XNodeWriter (XContainer fragment)
35                 {
36                         root = fragment;
37                         state = XmlNodeType.None;
38                         current = fragment;
39                 }
40
41                 XContainer root;
42                 bool is_closed;
43                 // If it is not null, then we are now inside the element.
44                 XContainer current;
45                 // If it is not null, then we are now inside the attribute.
46                 XAttribute attribute;
47
48                 // None: started or closed.
49                 // XmlDeclaration: after xmldecl. Never allow xmldecl.
50                 // DocumentType: after doctype. Never allow xmldecl and doctype.
51                 // Element: inside document element.
52                 // 
53                 XmlNodeType state;
54
55                 // Properties
56
57                 public override WriteState WriteState {
58                         get {
59                                 if (is_closed)
60                                         return WriteState.Closed;
61                                 if (attribute != null)
62                                         return WriteState.Attribute;
63
64                                 switch (state) {
65                                 case XmlNodeType.None:
66                                         return WriteState.Start;
67                                 case XmlNodeType.XmlDeclaration:
68                                         return WriteState.Prolog;
69                                 case XmlNodeType.DocumentType:
70                                         return WriteState.Element;
71                                 default:
72                                         return WriteState.Content;
73                                 }
74                         }
75                 }
76
77                 /*
78                 public override string XmlLang {
79                         get {
80                                 for (XElement n = current as XElement; n != null; n = n.Parent as XElement)
81                                         if (n.HasAttribute ("xml:lang"))
82                                                 return n.GetAttribute ("xml:lang");
83                                 return String.Empty;
84                         }
85                 }
86
87                 public override XmlSpace XmlSpace {
88                         get {
89                                 for (XElement n = current as XElement; n != null; n = n.Parent as XElement) {
90                                         string xs = n.GetAttribute ("xml:space");
91                                         switch (xs) {
92                                         case "preserve":
93                                                 return XmlSpace.Preserve;
94                                         case "default":
95                                                 return XmlSpace.Default;
96                                         case "":
97                                                 continue;
98                                         default:
99                                                 throw new InvalidOperationException (String.Format ("Invalid xml:space {0}.", xs));
100                                         }
101                                 }
102                                 return XmlSpace.None;
103                         }
104                 }
105                 */
106
107                 // Private Methods
108
109                 void CheckState ()
110                 {
111                         if (is_closed)
112                                 throw new InvalidOperationException ();
113
114                 }
115
116                 void WritePossiblyTopLevelNode (XNode n, bool possiblyAttribute)
117                 {
118                         CheckState ();
119                         if (!possiblyAttribute && attribute != null)
120                                 throw new InvalidOperationException (String.Format ("Current state is not acceptable for {0}.", n.NodeType));
121
122                         if (state != XmlNodeType.Element)
123                                 root.Add (n);
124                         else if (attribute != null)
125                                 attribute.Value += XUtil.ToString (n);
126                         else
127                                 current.Add (n);
128                         if (state == XmlNodeType.None)
129                                 state = XmlNodeType.XmlDeclaration;
130                 }
131
132                 // unlike other XmlWriters the callers must set xmlns
133                 // attribute to overwrite prefix.
134                 void FillXmlns (XElement el, string prefix, XNamespace xns)
135                 {
136                         if (xns == XNamespace.Xmlns)
137                                 // do nothing for xmlns attributes
138                                 return;
139                         if (prefix == null)
140                                 return;
141
142                         if (xns == XNamespace.None)
143                                 if (el.GetPrefixOfNamespace (xns) != prefix)
144                                         el.SetAttributeValue (prefix == String.Empty ? XNamespace.None.GetName ("xmlns") : XNamespace.Xmlns.GetName (prefix), xns.NamespaceName);
145                         else if (el.GetDefaultNamespace () != XNamespace.None)
146                                 el.SetAttributeValue (XNamespace.None.GetName ("xmlns"), xns.NamespaceName);
147                 }
148
149                 // Public Methods
150
151                 public override void Close ()
152                 {
153                         CheckState ();
154                         is_closed = true;
155                 }
156
157                 public override void Flush ()
158                 {
159                 }
160
161                 public override string LookupPrefix (string ns)
162                 {
163                         CheckState ();
164                         if (current == null)
165                                 throw new InvalidOperationException ();
166                         XElement el = (current as XElement) ?? current.Parent;
167                         return el != null ? el.GetPrefixOfNamespace (XNamespace.Get (ns)) : null;
168                 }
169
170                 // StartDocument
171
172                 public override void WriteStartDocument ()
173                 {
174                         WriteStartDocument (null);
175                 }
176
177                 public override void WriteStartDocument (bool standalone)
178                 {
179                         WriteStartDocument (standalone ? "yes" : "no");
180                 }
181
182                 void WriteStartDocument (string sddecl)
183                 {
184                         CheckState ();
185                         if (state != XmlNodeType.None)
186                                 throw new InvalidOperationException ("Current state is not acceptable for xmldecl.");
187                         XDocument doc = current as XDocument;
188                         if (doc == null)
189                                 throw new InvalidOperationException ("Only document node can accept xml declaration");
190
191                         doc.Declaration = new XDeclaration ("1.0", null, sddecl);
192                         state = XmlNodeType.XmlDeclaration;
193                 }
194                 
195                 // EndDocument
196                 
197                 public override void WriteEndDocument ()
198                 {
199                         CheckState ();
200
201                         is_closed = true;
202                 }
203
204                 // DocumentType
205                 public override void WriteDocType (string name, string publicId, string systemId, string internalSubset)
206                 {
207                         CheckState ();
208                         switch (state) {
209                         case XmlNodeType.None:
210                         case XmlNodeType.XmlDeclaration:
211                                 XDocument doc = current as XDocument;
212                                 if (doc == null)
213                                         throw new InvalidOperationException ("Only document node can accept doctype declaration");
214                                 doc.Add (new XDocumentType (name, publicId, systemId, internalSubset));
215                                 state = XmlNodeType.DocumentType;
216                                 break;
217                         default:
218                                 throw new InvalidOperationException ("Current state is not acceptable for doctype.");
219                         }
220                 }
221
222                 // StartElement
223
224                 public override void WriteStartElement (string prefix, string name, string ns)
225                 {
226                         CheckState ();
227
228                         XNamespace xns = XNamespace.Get (ns ?? String.Empty);
229                         XElement el = new XElement (xns.GetName (name));
230                         if (current == null) {
231                                 root.Add (el);
232                                 state = XmlNodeType.Element;
233                         } else {
234                                 current.Add (el);
235                                 state = XmlNodeType.Element;
236                         }
237
238                         FillXmlns (el, prefix, xns);
239
240                         current = el;
241                 }
242
243                 // EndElement
244
245                 public override void WriteEndElement ()
246                 {
247                         WriteEndElementInternal (false);
248                 }
249                 
250                 public override void WriteFullEndElement ()
251                 {
252                         WriteEndElementInternal (true);
253                 }
254
255                 void WriteEndElementInternal (bool forceFull)
256                 {
257                         CheckState ();
258                         if (current == null)
259                                 throw new InvalidOperationException ("Current state is not acceptable for endElement.");
260
261                         XElement el = current as XElement;
262                         if (forceFull)
263                                 el.IsEmpty = false;
264
265                         current = current.Parent;
266                 }
267
268                 // StartAttribute
269
270                 public override void WriteStartAttribute (string prefix, string name, string ns)
271                 {
272                         CheckState ();
273                         if (attribute != null)
274                                 throw new InvalidOperationException ("There is an open attribute.");
275                         XElement el = current as XElement;
276                         if (el == null)
277                                 throw new InvalidOperationException ("Current state is not acceptable for startAttribute.");
278                         if (prefix == null)
279                                 prefix = String.Empty;
280
281                         // special case: in XmlWriter context, xmlns="blah" is
282                         // passeed as localName = "xmlns", ns = w3c_xmlns.
283                         if (prefix.Length == 0 && name == "xmlns" && ns == XNamespace.Xmlns.NamespaceName)
284                                 ns = String.Empty;
285
286                         XNamespace xns = ns == null ? XNamespace.None : XNamespace.Get (ns);
287                         el.SetAttributeValue (xns.GetName (name), String.Empty);
288                         attribute = el.LastAttribute;
289                         FillXmlns (el, ns != null ? prefix : null, xns);
290                 }
291
292                 public override void WriteEndAttribute ()
293                 {
294                         CheckState ();
295                         if (attribute == null)
296                                 throw new InvalidOperationException ("Current state is not acceptable for startAttribute.");
297
298                         attribute = null;
299                 }
300
301                 public override void WriteCData (string data)
302                 {
303                         CheckState ();
304                         if (current == null)
305                                 throw new InvalidOperationException ("Current state is not acceptable for CDATAsection.");
306
307                         current.Add (new XCData (data));
308                 }
309
310                 public override void WriteComment (string comment)
311                 {
312                         WritePossiblyTopLevelNode (new XComment (comment), false);
313                 }
314
315                 public override void WriteProcessingInstruction (string name, string value)
316                 {
317                         WritePossiblyTopLevelNode (
318                                 new XProcessingInstruction (name, value), false);
319                 }
320
321                 public override void WriteEntityRef (string name)
322                 {
323                         throw new NotSupportedException ();
324                 }
325
326                 public override void WriteCharEntity (char c)
327                 {
328                         throw new NotSupportedException ();
329                 }
330
331                 public override void WriteWhitespace (string ws)
332                 {
333                         // FIXME: check whitespace
334                         WritePossiblyTopLevelNode (new XText (ws), true);
335                 }
336
337                 public override void WriteString (string data)
338                 {
339                         CheckState ();
340                         if (current == null)
341                                 throw new InvalidOperationException ("Current state is not acceptable for Text.");
342
343                         if (attribute != null)
344                                 attribute.Value += data;
345                         else
346                                 current.Add (data);
347                 }
348
349                 public override void WriteName (string name)
350                 {
351                         WriteString (name);
352                 }
353
354                 public override void WriteNmToken (string nmtoken)
355                 {
356                         WriteString (nmtoken);
357                 }
358
359                 public override void WriteQualifiedName (string name, string ns)
360                 {
361                         string prefix = LookupPrefix (ns);
362                         if (prefix == null)
363                                 throw new ArgumentException (String.Format ("Invalid namespace {0}", ns));
364                         if (prefix != String.Empty)
365                                 WriteString (name);
366                         else
367                                 WriteString (prefix + ":" + name);
368                 }
369
370                 public override void WriteChars (char [] chars, int start, int len)
371                 {
372                         WriteString (new string (chars, start, len));
373                 }
374
375                 public override void WriteRaw (string data)
376                 {
377                         // It never supports raw string.
378                         WriteString (data);
379                 }
380
381                 public override void WriteRaw (char [] chars, int start, int len)
382                 {
383                         // It never supports raw string.
384                         WriteChars (chars, start, len);
385                 }
386
387                 public override void WriteBase64 (byte [] data, int start, int len)
388                 {
389                         // It never supports raw string.
390                         WriteString (Convert.ToBase64String (data, start, len));
391                 }
392                 
393                 public override void WriteBinHex (byte [] data, int start, int len)
394                 {
395                         throw new NotImplementedException ();
396                 }
397
398                 public override void WriteSurrogateCharEntity (char c1, char c2)
399                 {
400                         throw new NotImplementedException ();
401                 }
402         }
403 }