2 // Copyright (C) 2010 Novell Inc. http://novell.com
3 // Copyright (C) 2012 Xamarin Inc. http://xamarin.com
5 // Permission is hereby granted, free of charge, to any person obtaining
6 // a copy of this software and associated documentation files (the
7 // "Software"), to deal in the Software without restriction, including
8 // without limitation the rights to use, copy, modify, merge, publish,
9 // distribute, sublicense, and/or sell copies of the Software, and to
10 // permit persons to whom the Software is furnished to do so, subject to
11 // the following conditions:
13 // The above copyright notice and this permission notice shall be
14 // included in all copies or substantial portions of the Software.
16 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
26 using System.Collections.Generic;
27 using System.ComponentModel;
28 using System.Globalization;
31 using System.Reflection;
33 using System.Windows.Markup;
35 using System.Xaml.Schema;
44 internal abstract class XamlWriterInternalBase
46 public XamlWriterInternalBase (XamlSchemaContext schemaContext, XamlWriterStateManager manager)
48 this.sctx = schemaContext;
49 this.manager = manager;
50 var p = new PrefixLookup (sctx) { IsCollectingNamespaces = true }; // it does not raise unknown namespace error.
51 service_provider = new ValueSerializerContext (p, schemaContext, AmbientProvider);
54 XamlSchemaContext sctx;
55 XamlWriterStateManager manager;
57 internal IValueSerializerContext service_provider;
59 internal ObjectState root_state;
60 internal Stack<ObjectState> object_states = new Stack<ObjectState> ();
61 internal PrefixLookup prefix_lookup {
62 get { return (PrefixLookup) service_provider.GetService (typeof (INamespacePrefixLookup)); }
65 List<NamespaceDeclaration> namespaces {
66 get { return prefix_lookup.Namespaces; }
69 internal virtual IAmbientProvider AmbientProvider {
73 internal class ObjectState
76 public bool IsGetObject;
77 public int PositionalParameterIndex = -1;
79 public string FactoryMethod;
81 public object KeyValue;
82 public List<MemberAndValue> WrittenProperties = new List<MemberAndValue> ();
83 public bool IsInstantiated;
84 public bool IsXamlWriterCreated; // affects AfterProperties() calls.
87 internal class MemberAndValue
89 public MemberAndValue (XamlMember xm)
94 public XamlMember Member;
96 public AllowedMemberLocations OccuredAs = AllowedMemberLocations.None;
99 public void CloseAll ()
101 while (object_states.Count > 0) {
102 switch (manager.State) {
103 case XamlWriteState.MemberDone:
104 case XamlWriteState.ObjectStarted: // StartObject without member
107 case XamlWriteState.ValueWritten:
108 case XamlWriteState.ObjectWritten:
109 case XamlWriteState.MemberStarted: // StartMember without content
110 manager.OnClosingItem ();
114 throw new NotImplementedException (manager.State.ToString ()); // there shouldn't be anything though
119 internal string GetPrefix (string ns)
121 foreach (var nd in namespaces)
122 if (nd.Namespace == ns)
127 protected MemberAndValue CurrentMemberState {
128 get { return object_states.Count > 0 ? object_states.Peek ().WrittenProperties.LastOrDefault () : null; }
131 protected XamlMember CurrentMember {
133 var mv = CurrentMemberState;
134 return mv != null ? mv.Member : null;
138 public void WriteGetObject ()
140 manager.GetObject ();
142 var xm = CurrentMember;
144 var state = new ObjectState () {Type = xm.Type, IsGetObject = true};
146 object_states.Push (state);
151 public void WriteNamespace (NamespaceDeclaration namespaceDeclaration)
153 if (namespaceDeclaration == null)
154 throw new ArgumentNullException ("namespaceDeclaration");
156 manager.Namespace ();
158 namespaces.Add (namespaceDeclaration);
159 OnWriteNamespace (namespaceDeclaration);
162 public void WriteStartObject (XamlType xamlType)
164 if (xamlType == null)
165 throw new ArgumentNullException ("xamlType");
167 manager.StartObject ();
169 var cstate = new ObjectState () {Type = xamlType};
170 object_states.Push (cstate);
172 OnWriteStartObject ();
175 public void WriteValue (object value)
179 OnWriteValue (value);
182 public void WriteStartMember (XamlMember property)
184 if (property == null)
185 throw new ArgumentNullException ("property");
187 manager.StartMember ();
188 if (property == XamlLanguage.PositionalParameters)
189 // this is an exception that indicates the state manager to accept more than values within this member.
190 manager.AcceptMultipleValues = true;
192 var state = object_states.Peek ();
193 var wpl = state.WrittenProperties;
194 if (wpl.Any (wp => wp.Member == property))
195 throw new XamlDuplicateMemberException (String.Format ("Property '{0}' is already set to this '{1}' object", property, object_states.Peek ().Type));
196 wpl.Add (new MemberAndValue (property));
197 if (property == XamlLanguage.PositionalParameters)
198 state.PositionalParameterIndex = 0;
200 OnWriteStartMember (property);
203 public void WriteEndObject ()
205 manager.EndObject (object_states.Count > 1);
209 object_states.Pop ();
212 public void WriteEndMember ()
214 manager.EndMember ();
218 var state = object_states.Peek ();
219 if (CurrentMember == XamlLanguage.PositionalParameters) {
220 manager.AcceptMultipleValues = false;
221 state.PositionalParameterIndex = -1;
225 protected abstract void OnWriteEndObject ();
227 protected abstract void OnWriteEndMember ();
229 protected abstract void OnWriteStartObject ();
231 protected abstract void OnWriteGetObject ();
233 protected abstract void OnWriteStartMember (XamlMember xm);
235 protected abstract void OnWriteValue (object value);
237 protected abstract void OnWriteNamespace (NamespaceDeclaration nd);
239 protected string GetValueString (XamlMember xm, object value)
241 // change XamlXmlReader too if we change here.
242 if ((value as string) == String.Empty) // FIXME: there could be some escape syntax.
245 var xt = value == null ? XamlLanguage.Null : sctx.GetXamlType (value.GetType ());
246 var vs = xm.ValueSerializer ?? xt.ValueSerializer;
248 return vs.ConverterInstance.ConvertToString (value, service_provider);
250 throw new XamlXmlWriterException (String.Format ("Value type is '{0}' but it must be either string or any type that is convertible to string indicated by TypeConverterAttribute.", value != null ? value.GetType () : null));