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.Windows.Markup;
29 public class XamlObjectWriter : XamlWriter, IXamlLineInfoConsumer
31 public XamlObjectWriter (XamlSchemaContext schemaContext)
32 : this (schemaContext, null)
36 public XamlObjectWriter (XamlSchemaContext schemaContext, XamlObjectWriterSettings settings)
38 if (schemaContext == null)
39 throw new ArgumentNullException ("schemaContext");
40 this.sctx = schemaContext;
41 this.settings = settings ?? new XamlObjectWriterSettings ();
44 XamlSchemaContext sctx;
45 XamlObjectWriterSettings settings;
47 XamlWriterStateManager manager = new XamlWriterStateManager<XamlObjectWriterException, XamlObjectWriterException> (false);
49 int line = -1, column = -1;
50 Stack<object> objects = new Stack<object> ();
51 Stack<XamlType> types = new Stack<XamlType> ();
52 Stack<XamlMember> members = new Stack<XamlMember> ();
54 List<object> arguments = new List<object> ();
55 string factory_method;
56 bool object_instantiated;
57 List<object> contents = new List<object> ();
58 List<object> objects_from_getter = new List<object> ();
59 Stack<List<XamlMember>> written_properties_stack = new Stack<List<XamlMember>> ();
61 public virtual object Result {
62 get { return result; }
65 public INameScope RootNameScope {
66 get { throw new NotImplementedException (); }
69 public override XamlSchemaContext SchemaContext {
73 public bool ShouldProvideLineInfo {
74 get { return line > 0 && column > 0; }
79 throw new NotImplementedException ();
82 protected override void Dispose (bool disposing)
87 while (types.Count > 0) {
94 protected virtual void OnAfterBeginInit (object value)
96 throw new NotImplementedException ();
99 protected virtual void OnAfterEndInit (object value)
101 throw new NotImplementedException ();
104 protected virtual void OnAfterProperties (object value)
106 throw new NotImplementedException ();
109 protected virtual void OnBeforeProperties (object value)
111 throw new NotImplementedException ();
114 protected virtual bool OnSetValue (object eventSender, XamlMember member, object value)
116 if (settings.XamlSetValueHandler != null) {
117 settings.XamlSetValueHandler (eventSender, new XamlSetValueEventArgs (member, value));
123 public void SetLineInfo (int lineNumber, int linePosition)
126 column = linePosition;
129 [MonoTODO ("Array and Dictionary needs implementation")]
130 public override void WriteEndMember ()
132 manager.EndMember ();
134 var xm = members.Pop ();
138 throw new NotImplementedException ();
139 } else if (xt.IsCollection) {
140 var obj = objects.Peek ();
141 foreach (var content in contents)
142 xt.Invoker.AddToCollection (obj, content);
143 } else if (xt.IsDictionary) {
144 throw new NotImplementedException ();
146 if (contents.Count > 1)
147 throw new XamlDuplicateMemberException (String.Format ("Value for {0} is assigned more than once", xm.Name));
148 if (contents.Count == 1) {
149 var value = GetCorrectlyTypedValue (xm, contents [0]);
150 if (!objects_from_getter.Remove (value))
151 if (!OnSetValue (this, xm, value))
152 xm.Invoker.SetValue (objects.Peek (), value);
157 written_properties_stack.Peek ().Add (xm);
160 object GetCorrectlyTypedValue (XamlMember xm, object value)
163 if (IsAllowedType (xt, value))
165 if (xt.TypeConverter != null && value != null) {
166 var tc = xt.TypeConverter.ConverterInstance;
167 if (tc != null && tc.CanConvertFrom (value.GetType ()))
168 value = tc.ConvertFrom (value);
169 if (IsAllowedType (xt, value))
172 throw new XamlObjectWriterException (String.Format ("Value is not of type {0}", xt));
175 bool IsAllowedType (XamlType xt, object value)
177 return xt == null || xt.UnderlyingType == null || xt.UnderlyingType.IsInstanceOfType (value);
180 public override void WriteEndObject ()
182 manager.EndObject (types.Count > 0);
184 InitializeObjectIfRequired (); // this is required for such case that there was no StartMember call.
187 written_properties_stack.Pop ();
188 var obj = objects.Pop ();
189 if (members.Count > 0)
193 public override void WriteGetObject ()
195 manager.GetObject ();
197 var xm = members.Peek ();
198 if (!xm.Type.IsCollection)
199 throw new XamlObjectWriterException (String.Format ("WriteGetObject method can be invoked only when current member '{0}' is of collection type", xm.Name));
201 var obj = xm.Invoker.GetValue (objects.Peek ());
203 throw new XamlObjectWriterException (String.Format ("The value for '{0}' property is null", xm.Name));
205 types.Push (SchemaContext.GetXamlType (obj.GetType ()));
206 ObjectInitialized (obj);
207 objects_from_getter.Add (obj);
210 public override void WriteNamespace (NamespaceDeclaration namespaceDeclaration)
212 if (namespaceDeclaration == null)
213 throw new ArgumentNullException ("namespaceDeclaration");
215 manager.Namespace ();
217 // FIXME: find out what to do.
220 public override void WriteStartMember (XamlMember property)
222 if (property == null)
223 throw new ArgumentNullException ("property");
225 manager.StartMember ();
227 var wpl = written_properties_stack.Peek ();
228 if (wpl.Contains (property))
229 throw new XamlDuplicateMemberException (String.Format ("Property '{0}' is already set to this '{1}' object", property.Name, types.Peek ().Name));
232 members.Push (property);
234 if (property == XamlLanguage.Initialization)
237 InitializeObjectIfRequired ();
240 void InitializeObjectIfRequired ()
242 if (object_instantiated)
245 // FIXME: "The default techniques in absence of a factory method are to attempt to find a default constructor, then attempt to find an identified type converter on type, member, or destination type."
246 // http://msdn.microsoft.com/en-us/library/system.xaml.xamllanguage.factorymethod%28VS.100%29.aspx
248 var args = arguments.ToArray ();
249 if (factory_method != null)
250 obj = types.Peek ().UnderlyingType.GetMethod (factory_method).Invoke (null, args);
252 obj = types.Peek ().Invoker.CreateInstance (args);
253 ObjectInitialized (obj);
256 public override void WriteStartObject (XamlType xamlType)
258 if (xamlType == null)
259 throw new ArgumentNullException ("xamlType");
261 manager.StartObject ();
263 types.Push (xamlType);
265 object_instantiated = false;
267 written_properties_stack.Push (new List<XamlMember> ());
270 public override void WriteValue (object value)
274 var xm = members.Peek ();
276 if (xm == XamlLanguage.Initialization)
277 ObjectInitialized (value);
278 else if (xm == XamlLanguage.Arguments)
279 arguments.Add (value);
280 else if (xm == XamlLanguage.FactoryMethod)
281 factory_method = (string) value;
283 contents.Add (value);
286 void ObjectInitialized (object obj)
289 object_instantiated = true;
290 if (objects.Count == 1)
291 result = objects.Peek ();
293 factory_method = null;