2 // System.Windows.ScriptableObjectGenerator class
5 // Moonlight List (moonlight-list@lists.ximian.com)
7 // Copyright (C) 2007 Novell, Inc (http://www.novell.com)
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.
30 using System
.Collections
.Generic
;
32 using System
.Windows
.Browser
;
33 using System
.Runtime
.InteropServices
;
34 using System
.Reflection
;
35 using System
.Globalization
;
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
)
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
));
64 public bool TryChangeType (object value, Type type
, CultureInfo culture
, out object ret
)
66 ScriptObject script_object
;
73 if (value.GetType() == type
)
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
);
82 if (value.GetType () == type
)
86 if (type
.IsAssignableFrom (value.GetType ()))
91 ret
= Enum
.Parse (type
, value.ToString(), true);
98 /* the set of source types for JS functions is
99 * very, very small, so we switch over the
100 * parameter type first */
102 ret
= Convert
.ChangeType (value, type
, culture
);
106 // no clue if this is right.. if we
107 // fail to convert, what do we return?
109 switch (Type
.GetTypeCode (type
))
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
);
124 case TypeCode
.String
:
128 case TypeCode
.Boolean
:
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
))
165 } else if (t
.IsArray
) {
166 return ValidateType (t
.GetElementType ());
168 if (ValidateProperties (t
) | ValidateMethods (t
) | ValidateEvents (t
))
176 static bool ValidateProperties (Type t
) {
179 foreach (PropertyInfo pi
in t
.GetProperties ()) {
180 if (!pi
.IsDefined (typeof(ScriptableMemberAttribute
), true))
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
));
193 static bool ValidateMethods (Type t
) {
196 foreach (MethodInfo mi
in t
.GetMethods ()) {
197 if (!mi
.IsDefined (typeof(ScriptableMemberAttribute
), true))
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",
219 static bool ValidateEvents (Type t
) {
222 foreach (EventInfo ei
in t
.GetEvents ()) {
223 if (!ei
.IsDefined (typeof(ScriptableMemberAttribute
), true))
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
));
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);
256 foreach (PropertyInfo pi
in type
.GetProperties ()) {
257 if (!isScriptable
&& !pi
.IsDefined (typeof(ScriptableMemberAttribute
), true))
259 scriptable
.AddProperty (pi
);
261 scriptable
.HasTypes
= true;
265 foreach (EventInfo ei
in type
.GetEvents ()) {
266 if (!isScriptable
&& !ei
.IsDefined (typeof(ScriptableMemberAttribute
), true))
268 scriptable
.AddEvent (ei
);
272 foreach (MethodInfo mi
in type
.GetMethods ()) {
273 if (!isScriptable
&& !mi
.IsDefined (typeof(ScriptableMemberAttribute
), true))
275 scriptable
.AddMethod (mi
);
277 scriptable
.HasTypes
= true;
280 if (scriptable
.HasTypes
)
281 scriptable
.AddMethod ("createManagedObject", new TypeCode
[]{TypeCode.String}
, TypeCode
.Object
);
286 static bool AddTypes (PropertyInfo pi
)
289 if (IsCreateable (pi
.PropertyType
)) {
291 AddType (pi
.PropertyType
);
296 static bool AddTypes (MethodInfo mi
)
299 if (IsCreateable (mi
.ReturnType
)) {
301 AddType (mi
.ReturnType
);
304 ParameterInfo
[] ps
= mi
.GetParameters();
305 foreach (ParameterInfo p
in ps
) {
306 if (IsCreateable (p
.ParameterType
)) {
308 AddType (p
.ParameterType
);
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
);
330 case TypeCode
.String
:
332 case TypeCode
.Boolean
:
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(!)
351 static bool IsCreateable (Type type
)
353 if (type
!= null && (Type
.GetTypeCode (type
) != TypeCode
.Object
|| type
== typeof (object)))
356 if (!type
.IsVisible
|| type
.IsAbstract
||
357 type
.IsInterface
|| type
.IsPrimitive
||
358 type
.IsGenericTypeDefinition
)
361 if (type
.IsValueType
)
364 // default constructor
365 if (type
.GetConstructor (BindingFlags
.Public
| BindingFlags
.Instance
, null, Type
.EmptyTypes
, null) == null)