2009-10-05 Chris Toshok <toshok@ximian.com>
[moon.git] / class / System.Windows.Browser / System.Windows / ScriptableObjectGenerator.cs
bloba161afff129a505868835537bfb90d4a93634709
1 //
2 // System.Windows.ScriptableObjectGenerator class
3 //
4 // Contact:
5 // Moonlight List (moonlight-list@lists.ximian.com)
6 //
7 // Copyright (C) 2007 Novell, Inc (http://www.novell.com)
8 //
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
29 using System;
30 using System.Collections.Generic;
31 using System.Text;
32 using System.Windows.Browser;
33 using System.Runtime.InteropServices;
34 using System.Reflection;
35 using System.Globalization;
36 using Mono;
38 namespace System.Windows
40 // XXX This class shouldn't be needed. MS just calls
41 // Convert.ChangeType on the arguments, and *somehow* gets the
42 // exception returned to JS. We don't do that yet, so
43 // unhandled exceptions crash the browser. In an effort to
44 // keep things limping along, we use this binder.
45 sealed class JSFriendlyMethodBinder : Binder {
46 public override FieldInfo BindToField (BindingFlags bindingAttr, FieldInfo [] match, object value, CultureInfo culture)
48 throw new NotImplementedException ();
51 public override MethodBase BindToMethod (BindingFlags bindingAttr, MethodBase [] match, ref object [] args, ParameterModifier [] modifiers, CultureInfo culture, string [] names, out object state)
53 throw new NotImplementedException ();
56 public override object ChangeType (object value, Type type, CultureInfo culture)
58 object ret;
59 if (!TryChangeType (value, type, culture, out ret))
60 throw new NotSupportedException (string.Format ("Can't change type from {0} to {1}", value.GetType ().FullName, type.FullName));
61 return ret;
64 public bool TryChangeType (object value, Type type, CultureInfo culture, out object ret)
66 ScriptObject script_object;
68 ret = value;
70 if (value == null)
71 return true;
73 if (value.GetType() == type)
74 return true;
76 script_object = value as ScriptObject;
77 if (script_object != null) {
78 value = script_object.ManagedObject;
79 if (value == null && type == typeof(HtmlElement))
80 value = new HtmlElement (script_object.Handle);
81 ret = value;
82 if (value.GetType () == type)
83 return true;
86 if (type.IsAssignableFrom (value.GetType ()))
87 return true;
89 if (type.IsEnum) {
90 try {
91 ret = Enum.Parse (type, value.ToString(), true);
92 return true;
93 } catch {
94 return false;
98 /* the set of source types for JS functions is
99 * very, very small, so we switch over the
100 * parameter type first */
101 try {
102 ret = Convert.ChangeType (value, type, culture);
103 return true;
105 catch {
106 // no clue if this is right.. if we
107 // fail to convert, what do we return?
109 switch (Type.GetTypeCode (type))
111 case TypeCode.Char:
112 case TypeCode.Byte:
113 case TypeCode.SByte:
114 case TypeCode.Int16:
115 case TypeCode.Int32:
116 case TypeCode.Int64:
117 case TypeCode.UInt16:
118 case TypeCode.UInt32:
119 case TypeCode.UInt64:
120 case TypeCode.Single:
121 case TypeCode.Double:
122 ret = Convert.ChangeType (0, type, culture);
123 return true;
124 case TypeCode.String:
125 ret = "";
126 return true;
128 case TypeCode.Boolean:
129 ret = false;
130 return true;
134 return false;
137 public override void ReorderArgumentArray (ref object [] args, object state)
139 throw new NotImplementedException ();
142 public override MethodBase SelectMethod (BindingFlags bindingAttr, MethodBase [] match, Type [] types, ParameterModifier [] modifiers)
144 throw new NotImplementedException ();
147 public override PropertyInfo SelectProperty (BindingFlags bindingAttr, PropertyInfo [] match, Type returnType, Type [] indexes, ParameterModifier [] modifiers)
149 throw new NotImplementedException ();
154 internal static class ScriptableObjectGenerator
156 public static bool ValidateType (Type t)
158 if (!t.IsDefined (typeof(ScriptableTypeAttribute), true)) {
159 if (t.IsGenericType) {
160 foreach (Type type in t.GetGenericArguments ()) {
161 if (!ValidateType (type))
162 return false;
164 return true;
165 } else if (t.IsArray) {
166 return ValidateType (t.GetElementType ());
167 } else {
168 if (ValidateProperties (t) | ValidateMethods (t) | ValidateEvents (t))
169 return true;
171 } else
172 return true;
173 return false;
176 static bool ValidateProperties (Type t) {
177 bool ret = false;
179 foreach (PropertyInfo pi in t.GetProperties ()) {
180 if (!pi.IsDefined (typeof(ScriptableMemberAttribute), true))
181 continue;
183 if (pi.PropertyType != t && !IsSupportedType (pi.PropertyType)) {
184 throw new NotSupportedException (
185 String.Format ("The scriptable object type {0} has a property {1} whose type {2} is not supported",
186 t, pi, pi.PropertyType));
188 ret = true;
190 return ret;
193 static bool ValidateMethods (Type t) {
194 bool ret = false;
196 foreach (MethodInfo mi in t.GetMethods ()) {
197 if (!mi.IsDefined (typeof(ScriptableMemberAttribute), true))
198 continue;
200 if (mi.ReturnType != typeof (void) && mi.ReturnType != t && !IsSupportedType (mi.ReturnType))
201 throw new NotSupportedException (
202 String.Format ("The scriptable object type {0} has a method {1} whose return type {2} is not supported",
203 t, mi, mi.ReturnType));
205 ParameterInfo[] ps = mi.GetParameters();
206 foreach (ParameterInfo p in ps) {
207 if (p.IsOut || (p.ParameterType != t && !IsSupportedType (p.ParameterType)))
208 throw new NotSupportedException (
209 String.Format ("The scriptable object type {0} has a method {1} whose parameter {2} is of not supported type",
210 t, mi, p));
213 ret = true;
216 return ret;
219 static bool ValidateEvents (Type t) {
220 bool ret = false;
222 foreach (EventInfo ei in t.GetEvents ()) {
223 if (!ei.IsDefined (typeof(ScriptableMemberAttribute), true))
224 continue;
226 // Console.WriteLine ("event handler type = {0}", ei.EventHandlerType);
227 // Console.WriteLine ("typeof (EventHandler<>) == {0}", typeof (EventHandler<>));
229 if (ei.EventHandlerType != typeof (EventHandler) &&
230 typeof (EventHandler<>).IsAssignableFrom (ei.EventHandlerType)) {
231 if (!ValidateType (ei.EventHandlerType)) {
232 throw new NotSupportedException (
233 String.Format ("The scriptable object type {0} has a event {1} whose type {2} is not supported",
234 t, ei, ei.EventHandlerType));
238 ret = true;
240 return ret;
243 public static ScriptableObjectWrapper Generate (object instance, bool validate)
245 Type type = instance.GetType ();
247 if (validate && !ValidateType (type))
248 throw new ArgumentException (String.Format ("The scriptable type {0} does not have scriptable members", type));
250 ScriptableObjectWrapper scriptable = new ScriptableObjectWrapper (instance);
252 bool isScriptable = type.IsDefined (typeof(ScriptableTypeAttribute), true);
254 // add properties
256 foreach (PropertyInfo pi in type.GetProperties ()) {
257 if (!isScriptable && !pi.IsDefined (typeof(ScriptableMemberAttribute), true))
258 continue;
259 scriptable.AddProperty (pi);
260 if (AddTypes (pi))
261 scriptable.HasTypes = true;
264 // add events
265 foreach (EventInfo ei in type.GetEvents ()) {
266 if (!isScriptable && !ei.IsDefined (typeof(ScriptableMemberAttribute), true))
267 continue;
268 scriptable.AddEvent (ei);
271 // add functions
272 foreach (MethodInfo mi in type.GetMethods ()) {
273 if (!isScriptable && !mi.IsDefined (typeof(ScriptableMemberAttribute), true))
274 continue;
275 scriptable.AddMethod (mi);
276 if (AddTypes (mi))
277 scriptable.HasTypes = true;
280 if (scriptable.HasTypes)
281 scriptable.AddMethod ("createManagedObject", new TypeCode[]{TypeCode.String}, TypeCode.Object);
283 return scriptable;
286 static bool AddTypes (PropertyInfo pi)
288 bool ret = false;
289 if (IsCreateable (pi.PropertyType)) {
290 ret = true;
291 AddType (pi.PropertyType);
293 return ret;
296 static bool AddTypes (MethodInfo mi)
298 bool ret = false;
299 if (IsCreateable (mi.ReturnType)) {
300 ret = true;
301 AddType (mi.ReturnType);
304 ParameterInfo[] ps = mi.GetParameters();
305 foreach (ParameterInfo p in ps) {
306 if (IsCreateable (p.ParameterType)) {
307 ret = true;
308 AddType (p.ParameterType);
311 return ret;
314 static void AddType (Type type)
316 if (!HtmlPage.ScriptableTypes.ContainsKey (type.Name))
317 HtmlPage.ScriptableTypes[type.Name] = type;
320 static bool IsSupportedType (Type t)
322 TypeCode tc = Type.GetTypeCode (t);
323 if (tc == TypeCode.Object) {
324 return ValidateType (t);
327 switch (tc) {
328 // string
329 case TypeCode.Char:
330 case TypeCode.String:
331 // boolean
332 case TypeCode.Boolean:
333 // number
334 case TypeCode.Byte:
335 case TypeCode.SByte:
336 case TypeCode.Int16:
337 case TypeCode.Int32:
338 case TypeCode.Int64:
339 case TypeCode.UInt16:
340 case TypeCode.UInt32:
341 case TypeCode.UInt64:
342 case TypeCode.Single:
343 case TypeCode.Double:
344 // case TypeCode.Decimal: // decimal is unsupported(!)
345 return true;
348 return false;
351 static bool IsCreateable (Type type)
353 if (type != null && (Type.GetTypeCode (type) != TypeCode.Object || type == typeof (object)))
354 return false;
356 if (!type.IsVisible || type.IsAbstract ||
357 type.IsInterface || type.IsPrimitive ||
358 type.IsGenericTypeDefinition)
359 return false;
361 if (type.IsValueType)
362 return false;
364 // default constructor
365 if (type.GetConstructor (BindingFlags.Public | BindingFlags.Instance, null, Type.EmptyTypes, null) == null)
366 return false;
368 return true;