New tests.
[mono.git] / mcs / class / System.Xaml / System.Xaml / XamlObjectWriter.cs
1 //
2 // Copyright (C) 2010 Novell Inc. http://novell.com
3 //
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:
11 // 
12 // The above copyright notice and this permission notice shall be
13 // included in all copies or substantial portions of the Software.
14 // 
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.
22 //
23 using System;
24 using System.Collections.Generic;
25 using System.Windows.Markup;
26
27 namespace System.Xaml
28 {
29         public class XamlObjectWriter : XamlWriter, IXamlLineInfoConsumer
30         {
31                 public XamlObjectWriter (XamlSchemaContext schemaContext)
32                         : this (schemaContext, null)
33                 {
34                 }
35
36                 public XamlObjectWriter (XamlSchemaContext schemaContext, XamlObjectWriterSettings settings)
37                 {
38                         if (schemaContext == null)
39                                 throw new ArgumentNullException ("schemaContext");
40                         this.sctx = schemaContext;
41                         this.settings = settings ?? new XamlObjectWriterSettings ();
42                 }
43
44                 XamlSchemaContext sctx;
45                 XamlObjectWriterSettings settings;
46
47                 XamlWriterStateManager manager = new XamlWriterStateManager<XamlObjectWriterException, XamlObjectWriterException> (false);
48                 object result;
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> ();
53
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>> ();
60
61                 public virtual object Result {
62                         get { return result; }
63                 }
64
65                 public INameScope RootNameScope {
66                         get { throw new NotImplementedException (); }
67                 }
68
69                 public override XamlSchemaContext SchemaContext {
70                         get { return sctx; }
71                 }
72
73                 public bool ShouldProvideLineInfo {
74                         get { return line > 0 && column > 0; }
75                 }
76                 
77                 public void Clear ()
78                 {
79                         throw new NotImplementedException ();
80                 }
81
82                 protected override void Dispose (bool disposing)
83                 {
84                         if (!disposing)
85                                 return;
86
87                         while (types.Count > 0) {
88                                 WriteEndObject ();
89                                 if (types.Count > 0)
90                                         WriteEndMember ();
91                         }
92                 }
93
94                 protected virtual void OnAfterBeginInit (object value)
95                 {
96                         throw new NotImplementedException ();
97                 }
98
99                 protected virtual void OnAfterEndInit (object value)
100                 {
101                         throw new NotImplementedException ();
102                 }
103
104                 protected virtual void OnAfterProperties (object value)
105                 {
106                         throw new NotImplementedException ();
107                 }
108
109                 protected virtual void OnBeforeProperties (object value)
110                 {
111                         throw new NotImplementedException ();
112                 }
113
114                 protected virtual bool OnSetValue (object eventSender, XamlMember member, object value)
115                 {
116                         if (settings.XamlSetValueHandler != null) {
117                                 settings.XamlSetValueHandler (eventSender, new XamlSetValueEventArgs (member, value));
118                                 return true;
119                         }
120                         return false;
121                 }
122
123                 public void SetLineInfo (int lineNumber, int linePosition)
124                 {
125                         line = lineNumber;
126                         column = linePosition;
127                 }
128
129                 [MonoTODO ("Array and Dictionary needs implementation")]
130                 public override void WriteEndMember ()
131                 {
132                         manager.EndMember ();
133                         
134                         var xm = members.Pop ();
135                         var xt = xm.Type;
136
137                         if (xt.IsArray) {
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 ();
145                         } else {
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);
153                                 }
154                         }
155
156                         contents.Clear ();
157                         written_properties_stack.Peek ().Add (xm);
158                 }
159
160                 object GetCorrectlyTypedValue (XamlMember xm, object value)
161                 {
162                         var xt = xm.Type;
163                         if (IsAllowedType (xt, value))
164                                 return 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))
170                                         return value;
171                         }
172                         throw new XamlObjectWriterException (String.Format ("Value is not of type {0}", xt));
173                 }
174
175                 bool IsAllowedType (XamlType xt, object value)
176                 {
177                         return xt == null || xt.UnderlyingType == null || xt.UnderlyingType.IsInstanceOfType (value);
178                 }
179
180                 public override void WriteEndObject ()
181                 {
182                         manager.EndObject (types.Count > 0);
183
184                         InitializeObjectIfRequired (); // this is required for such case that there was no StartMember call.
185
186                         types.Pop ();
187                         written_properties_stack.Pop ();
188                         var obj = objects.Pop ();
189                         if (members.Count > 0)
190                                 contents.Add (obj);
191                 }
192
193                 public override void WriteGetObject ()
194                 {
195                         manager.GetObject ();
196
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));
200
201                         var obj = xm.Invoker.GetValue (objects.Peek ());
202                         if (obj == null)
203                                 throw new XamlObjectWriterException (String.Format ("The value  for '{0}' property is null", xm.Name));
204
205                         types.Push (SchemaContext.GetXamlType (obj.GetType ()));
206                         ObjectInitialized (obj);
207                         objects_from_getter.Add (obj);
208                 }
209
210                 public override void WriteNamespace (NamespaceDeclaration namespaceDeclaration)
211                 {
212                         if (namespaceDeclaration == null)
213                                 throw new ArgumentNullException ("namespaceDeclaration");
214
215                         manager.Namespace ();
216
217                         // FIXME: find out what to do.
218                 }
219
220                 public override void WriteStartMember (XamlMember property)
221                 {
222                         if (property == null)
223                                 throw new ArgumentNullException ("property");
224
225                         manager.StartMember ();
226
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));
230                         wpl.Add (property);
231
232                         members.Push (property);
233
234                         if (property == XamlLanguage.Initialization)
235                                 return;
236                         else
237                                 InitializeObjectIfRequired ();
238                 }
239
240                 void InitializeObjectIfRequired ()
241                 {
242                         if (object_instantiated)
243                                 return;
244
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
247                         object obj;
248                         var args = arguments.ToArray ();
249                         if (factory_method != null)
250                                 obj = types.Peek ().UnderlyingType.GetMethod (factory_method).Invoke (null, args);
251                         else
252                                 obj = types.Peek ().Invoker.CreateInstance (args);
253                         ObjectInitialized (obj);
254                 }
255
256                 public override void WriteStartObject (XamlType xamlType)
257                 {
258                         if (xamlType == null)
259                                 throw new ArgumentNullException ("xamlType");
260
261                         manager.StartObject ();
262
263                         types.Push (xamlType);
264
265                         object_instantiated = false;
266
267                         written_properties_stack.Push (new List<XamlMember> ());
268                 }
269
270                 public override void WriteValue (object value)
271                 {
272                         manager.Value ();
273
274                         var xm = members.Peek ();
275
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;
282                         else
283                                 contents.Add (value);
284                 }
285
286                 void ObjectInitialized (object obj)
287                 {
288                         objects.Push (obj);
289                         object_instantiated = true;
290                         if (objects.Count == 1)
291                                 result = objects.Peek ();
292                         arguments.Clear ();
293                         factory_method = null;
294                 }
295         }
296 }