2 // Copyright (C) 2010 Novell Inc. http://novell.com
4 // Permission is hereby granted, free of charge, to any person obtaining
5 // a copy of this software and associated documentation files (the
6 // "Software"), to deal in the Software without restriction, including
7 // without limitation the rights to use, copy, modify, merge, publish,
8 // distribute, sublicense, and/or sell copies of the Software, and to
9 // permit persons to whom the Software is furnished to do so, subject to
10 // the following conditions:
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
15 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 using System.Collections.Generic;
25 using System.ComponentModel;
28 using System.Windows.Markup;
33 ** Value output node type
35 When an object contains a member:
36 - it becomes an attribute when it contains a value.
37 - it becomes an element when it contains an object.
42 public class XamlXmlWriter : XamlWriter
44 public XamlXmlWriter (Stream stream, XamlSchemaContext schemaContext)
45 : this (stream, schemaContext, null)
49 public XamlXmlWriter (Stream stream, XamlSchemaContext schemaContext, XamlXmlWriterSettings settings)
50 : this (XmlWriter.Create (stream), schemaContext, null)
54 public XamlXmlWriter (TextWriter textWriter, XamlSchemaContext schemaContext)
55 : this (XmlWriter.Create (textWriter), schemaContext, null)
59 public XamlXmlWriter (TextWriter textWriter, XamlSchemaContext schemaContext, XamlXmlWriterSettings settings)
60 : this (XmlWriter.Create (textWriter), schemaContext, null)
64 public XamlXmlWriter (XmlWriter xmlWriter, XamlSchemaContext schemaContext)
65 : this (xmlWriter, schemaContext, null)
69 public XamlXmlWriter (XmlWriter xmlWriter, XamlSchemaContext schemaContext, XamlXmlWriterSettings settings)
71 if (xmlWriter == null)
72 throw new ArgumentNullException ("xmlWriter");
73 if (schemaContext == null)
74 throw new ArgumentNullException ("schemaContext");
76 this.sctx = schemaContext;
77 this.settings = settings ?? new XamlXmlWriterSettings ();
78 this.manager = new XamlWriterStateManager<XamlXmlWriterException, InvalidOperationException> (true);
82 XamlSchemaContext sctx;
83 XamlXmlWriterSettings settings;
84 IValueSerializerContext serializer_context;
86 XamlWriterStateManager manager;
88 Stack<object> nodes = new Stack<object> ();
89 bool is_first_member_content, has_namespace;
90 object first_member_value;
92 public override XamlSchemaContext SchemaContext {
96 public XamlXmlWriterSettings Settings {
97 get { return settings; }
100 protected override void Dispose (bool disposing)
105 while (nodes.Count > 0) {
106 var obj = nodes.Peek ();
107 if (obj is XamlMember) {
108 manager.OnClosingItem ();
111 else if (obj is XamlType)
116 if (settings.CloseOutput)
125 public override void WriteEndMember ()
127 manager.EndMember ();
128 WriteStackedStartMember (XamlNodeType.EndMember);
133 public override void WriteEndObject ()
135 manager.EndObject (nodes.Count > 1);
136 w.WriteEndElement ();
140 public override void WriteGetObject ()
142 manager.GetObject ();
143 WriteStackedStartMember (XamlNodeType.GetObject);
145 var xm = (XamlMember) GetNonNamespaceNode ();
146 if (!xm.Type.IsCollection)
147 throw new InvalidOperationException (String.Format ("WriteGetObject method can be invoked only when current member '{0}' is of collection type", xm.Name));
151 // FIXME: it likely has to write the "retrieved" object here.
154 public override void WriteNamespace (NamespaceDeclaration namespaceDeclaration)
156 if (namespaceDeclaration == null)
157 throw new ArgumentNullException ("namespaceDeclaration");
159 manager.Namespace ();
161 nodes.Push (namespaceDeclaration);
162 has_namespace = true;
165 public override void WriteStartMember (XamlMember property)
167 if (property == null)
168 throw new ArgumentNullException ("property");
170 manager.StartMember ();
171 nodes.Push (property);
173 is_first_member_content = true;
176 public override void WriteStartObject (XamlType xamlType)
178 if (xamlType == null)
179 throw new ArgumentNullException ("xamlType");
181 manager.StartObject ();
183 WriteStackedStartMember (XamlNodeType.StartObject);
185 nodes.Push (xamlType);
186 DoWriteStartObject (xamlType);
189 public override void WriteValue (object value)
193 var xt = GetCurrentType ();
195 var xm = GetNonNamespaceNode () as XamlMember;
196 if (xm == XamlLanguage.Initialization) {
197 // do not reject type mismatch, as the value will be a string.
199 else if (xt != null && xt.UnderlyingType != null && !xt.UnderlyingType.IsInstanceOfType (value))
200 throw new ArgumentException (String.Format ("Value is not of type {0} but {1}", xt, value != null ? value.GetType ().FullName : "(null)"));
202 if (!is_first_member_content) {
203 WriteStackedStartMember (XamlNodeType.Value);
204 DoWriteValue (value);
207 first_member_value = value;
212 var xm = nodes.Pop (); // XamlMember
213 if (xm == XamlLanguage.Initialization)
215 else if (w.WriteState == WriteState.Content)
216 w.WriteEndElement ();
218 w.WriteEndAttribute ();
220 is_first_member_content = false;
221 first_member_value = null;
224 void WriteStackedStartMember (XamlNodeType next)
226 if (!is_first_member_content)
229 var xm = GetNonNamespaceNode () as XamlMember;
233 if (xm == XamlLanguage.Initialization)
235 else if (next == XamlNodeType.StartObject || w.WriteState == WriteState.Content || has_namespace)
236 DoWriteStartMemberElement (xm);
238 DoWriteStartMemberAttribute (xm);
239 if (first_member_value != null)
240 DoWriteValue (first_member_value);
241 is_first_member_content = false;
244 void DoWriteStartObject (XamlType xamlType)
246 string prefix = GetPrefix (xamlType.PreferredXamlNamespace);
247 w.WriteStartElement (prefix, xamlType.Name, xamlType.PreferredXamlNamespace);
248 WriteAndClearNamespaces ();
251 void DoWriteStartMemberElement (XamlMember xm)
253 var xt = GetCurrentType ();
254 string prefix = GetPrefix (xm.PreferredXamlNamespace);
255 w.WriteStartElement (prefix, String.Concat (xt.Name, ".", xm.Name), xm.PreferredXamlNamespace);
256 WriteAndClearNamespaces ();
259 void DoWriteStartMemberAttribute (XamlMember xm)
261 WriteAndClearNamespaces ();
263 var xt = GetCurrentType ();
264 if (xt.PreferredXamlNamespace == xm.PreferredXamlNamespace)
265 w.WriteStartAttribute (xm.Name);
267 string prefix = GetPrefix (xm.PreferredXamlNamespace);
268 w.WriteStartAttribute (prefix, xm.Name, xm.PreferredXamlNamespace);
272 void DoWriteValue (object value)
274 var xt = value == null ? XamlLanguage.Null : SchemaContext.GetXamlType (value.GetType ());
275 var vs = xt.TypeConverter;
276 var c = vs != null ? vs.ConverterInstance : null;
277 if (c != null && c.CanConvertTo (typeof (string)))
278 w.WriteString (c.ConvertToInvariantString (value));
280 w.WriteValue (value);
283 object GetNonNamespaceNode ()
285 if (nodes.Count == 0)
287 var obj = nodes.Pop ();
289 if (obj is NamespaceDeclaration)
290 return GetNonNamespaceNode ();
298 XamlType GetCurrentType ()
300 if (nodes.Count == 0)
302 var obj = nodes.Pop ();
305 return (XamlType) obj;
307 return GetCurrentType ();
313 string GetPrefix (string ns)
315 var decl = nodes.LastOrDefault (d => d is NamespaceDeclaration && ((NamespaceDeclaration) d).Namespace == ns) as NamespaceDeclaration;
318 return w.LookupPrefix (ns);
321 Stack<NamespaceDeclaration> tmp_nss = new Stack<NamespaceDeclaration> ();
323 void WriteAndClearNamespaces ()
325 // write namespace that are put *before* current item.
327 var top = nodes.Pop (); // temporarily pop out
329 while (nodes.Count > 0) {
330 var obj = nodes.Pop ();
331 var nd = obj as NamespaceDeclaration;
338 while (tmp_nss.Count > 0) {
339 var nd = tmp_nss.Pop ();
340 DoWriteNamespace (nd);
342 has_namespace = false;
343 manager.NamespaceCleanedUp ();
345 nodes.Push (top); // push back
348 void DoWriteNamespace (NamespaceDeclaration nd)
350 if (String.IsNullOrEmpty (nd.Prefix))
351 w.WriteAttributeString ("xmlns", nd.Namespace);
353 w.WriteAttributeString ("xmlns", nd.Prefix, XamlLanguage.Xmlns2000Namespace, nd.Namespace);