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.ComponentModel;
26 using System.Reflection;
27 using System.Windows.Markup;
28 using System.Xaml.Schema;
32 public class XamlMember : IEquatable<XamlMember>
34 public XamlMember (EventInfo eventInfo, XamlSchemaContext schemaContext)
35 : this (eventInfo, schemaContext, null)
39 public XamlMember (EventInfo eventInfo, XamlSchemaContext schemaContext, XamlMemberInvoker invoker)
40 : this (schemaContext, invoker)
42 if (eventInfo == null)
43 throw new ArgumentNullException ("eventInfo");
44 Name = eventInfo.Name;
45 underlying_member = eventInfo;
46 DeclaringType = schemaContext.GetXamlType (eventInfo.DeclaringType);
47 target_type = DeclaringType;
48 UnderlyingSetter = eventInfo.GetAddMethod ();
52 public XamlMember (PropertyInfo propertyInfo, XamlSchemaContext schemaContext)
53 : this (propertyInfo, schemaContext, null)
57 public XamlMember (PropertyInfo propertyInfo, XamlSchemaContext schemaContext, XamlMemberInvoker invoker)
58 : this (schemaContext, invoker)
60 if (propertyInfo == null)
61 throw new ArgumentNullException ("propertyInfo");
62 Name = propertyInfo.Name;
63 underlying_member = propertyInfo;
64 DeclaringType = schemaContext.GetXamlType (propertyInfo.DeclaringType);
65 target_type = DeclaringType;
66 UnderlyingGetter = propertyInfo.GetGetMethod (true);
67 UnderlyingSetter = propertyInfo.GetSetMethod (true);
70 public XamlMember (string attachableEventName, MethodInfo adder, XamlSchemaContext schemaContext)
71 : this (attachableEventName, adder, schemaContext, null)
75 public XamlMember (string attachableEventName, MethodInfo adder, XamlSchemaContext schemaContext, XamlMemberInvoker invoker)
76 : this (schemaContext, invoker)
78 if (attachableEventName == null)
79 throw new ArgumentNullException ("attachableEventName");
81 throw new ArgumentNullException ("adder");
82 Name = attachableEventName;
83 VerifyAdderSetter (adder);
84 underlying_member = adder;
85 DeclaringType = schemaContext.GetXamlType (adder.DeclaringType);
86 target_type = schemaContext.GetXamlType (typeof (object));
87 UnderlyingSetter = adder;
92 public XamlMember (string attachablePropertyName, MethodInfo getter, MethodInfo setter, XamlSchemaContext schemaContext)
93 : this (attachablePropertyName, getter, setter, schemaContext, null)
97 public XamlMember (string attachablePropertyName, MethodInfo getter, MethodInfo setter, XamlSchemaContext schemaContext, XamlMemberInvoker invoker)
98 : this (schemaContext, invoker)
100 if (attachablePropertyName == null)
101 throw new ArgumentNullException ("attachablePropertyName");
102 if (getter == null && setter == null)
103 throw new ArgumentNullException ("getter", "Either property getter or setter must be non-null.");
104 Name = attachablePropertyName;
105 VerifyGetter (getter);
106 VerifyAdderSetter (setter);
107 underlying_member = getter ?? setter;
108 DeclaringType = schemaContext.GetXamlType (underlying_member.DeclaringType);
109 target_type = schemaContext.GetXamlType (typeof (object));
110 UnderlyingGetter = getter;
111 UnderlyingSetter = setter;
112 is_attachable = true;
115 public XamlMember (string name, XamlType declaringType, bool isAttachable)
118 throw new ArgumentNullException ("name");
119 if (declaringType == null)
120 throw new ArgumentNullException ("declaringType");
122 this.invoker = new XamlMemberInvoker (this);
123 context = declaringType.SchemaContext;
124 DeclaringType = declaringType;
125 target_type = DeclaringType;
126 is_attachable = isAttachable;
129 XamlMember (XamlSchemaContext schemaContext, XamlMemberInvoker invoker)
131 if (schemaContext == null)
132 throw new ArgumentNullException ("schemaContext");
133 context = schemaContext;
134 this.invoker = invoker ?? new XamlMemberInvoker (this);
137 internal XamlMember (bool isDirective, string ns, string name)
141 is_directive = isDirective;
144 XamlType type, target_type;
145 MemberInfo underlying_member;
146 MethodInfo underlying_getter, underlying_setter;
147 XamlSchemaContext context;
148 XamlMemberInvoker invoker;
149 bool is_attachable, is_event, is_directive;
150 bool is_predefined_directive = XamlLanguage.InitializingDirectives;
153 internal MethodInfo UnderlyingGetter {
154 get { return LookupUnderlyingGetter (); }
155 private set { underlying_getter = value; }
157 internal MethodInfo UnderlyingSetter {
158 get { return LookupUnderlyingSetter (); }
159 private set { underlying_setter = value; }
162 public XamlType DeclaringType { get; private set; }
163 public string Name { get; private set; }
165 public string PreferredXamlNamespace {
166 get { return directive_ns ?? (DeclaringType == null ? null : DeclaringType.PreferredXamlNamespace); }
170 public DesignerSerializationVisibility SerializationVisibility {
172 var c= GetCustomAttributeProvider ();
173 var a = c == null ? null : c.GetCustomAttribute<DesignerSerializationVisibilityAttribute> (false);
174 return a != null ? a.Visibility : DesignerSerializationVisibility.Visible;
179 public bool IsAttachable {
180 get { return is_attachable; }
183 public bool IsDirective {
184 get { return is_directive; }
187 public bool IsNameValid {
188 get { return XamlLanguage.IsValidXamlName (Name); }
191 public XamlValueConverter<XamlDeferringLoader> DeferringLoader {
192 get { return LookupDeferringLoader (); }
195 static readonly XamlMember [] empty_members = new XamlMember [0];
197 public IList<XamlMember> DependsOn {
198 get { return LookupDependsOn () ?? empty_members; }
201 public XamlMemberInvoker Invoker {
202 get { return LookupInvoker (); }
204 public bool IsAmbient {
205 get { return LookupIsAmbient (); }
207 public bool IsEvent {
208 get { return LookupIsEvent (); }
210 public bool IsReadOnly {
211 get { return LookupIsReadOnly (); }
213 public bool IsReadPublic {
214 get { return LookupIsReadPublic (); }
216 public bool IsUnknown {
217 get { return LookupIsUnknown (); }
219 public bool IsWriteOnly {
220 get { return LookupIsWriteOnly (); }
222 public bool IsWritePublic {
223 get { return LookupIsWritePublic (); }
225 public XamlType TargetType {
226 get { return LookupTargetType (); }
228 public XamlType Type {
229 get { return LookupType (); }
231 public XamlValueConverter<TypeConverter> TypeConverter {
232 get { return LookupTypeConverter (); }
234 public MemberInfo UnderlyingMember {
235 get { return LookupUnderlyingMember (); }
237 public XamlValueConverter<ValueSerializer> ValueSerializer {
238 get { return LookupValueSerializer (); }
241 public static bool operator == (XamlMember left, XamlMember right)
243 return IsNull (left) ? IsNull (right) : left.Equals (right);
246 static bool IsNull (XamlMember a)
248 return Object.ReferenceEquals (a, null);
251 public static bool operator != (XamlMember left, XamlMember right)
253 return !(left == right);
256 public override bool Equals (object other)
258 var x = other as XamlMember;
262 public bool Equals (XamlMember other)
264 // this should be in general correct; XamlMembers are almost not comparable.
265 if (Object.ReferenceEquals (this, other))
267 // It does not compare XamlSchemaContext.
268 return !IsNull (other) &&
269 underlying_member == other.underlying_member &&
270 underlying_getter == other.underlying_getter &&
271 underlying_setter == other.underlying_setter &&
272 Name == other.Name &&
273 PreferredXamlNamespace == other.PreferredXamlNamespace &&
274 directive_ns == other.directive_ns &&
275 is_attachable == other.is_attachable;
278 public override int GetHashCode ()
280 return ToString ().GetHashCode (); // should in general work.
283 [MonoTODO ("there are some patterns that return different kind of value: e.g. List<int>.Capacity")]
284 public override string ToString ()
286 if (is_attachable || String.IsNullOrEmpty (PreferredXamlNamespace)) {
287 if (DeclaringType == null)
290 return String.Concat (DeclaringType.UnderlyingType.FullName, ".", Name);
293 return String.Concat ("{", PreferredXamlNamespace, "}", DeclaringType.Name, ".", Name);
296 public virtual IList<string> GetXamlNamespaces ()
298 throw new NotImplementedException ();
303 internal ICustomAttributeProvider GetCustomAttributeProvider ()
305 return LookupCustomAttributeProvider ();
308 protected virtual ICustomAttributeProvider LookupCustomAttributeProvider ()
310 return UnderlyingMember;
313 protected virtual XamlValueConverter<XamlDeferringLoader> LookupDeferringLoader ()
315 // FIXME: use XamlDeferLoadAttribute.
319 static readonly XamlMember [] empty_list = new XamlMember [0];
321 protected virtual IList<XamlMember> LookupDependsOn ()
326 protected virtual XamlMemberInvoker LookupInvoker ()
330 protected virtual bool LookupIsAmbient ()
332 var t = Type != null ? Type.UnderlyingType : null;
333 return t != null && t.GetCustomAttributes (typeof (AmbientAttribute), false).Length > 0;
336 protected virtual bool LookupIsEvent ()
341 protected virtual bool LookupIsReadOnly ()
343 return UnderlyingGetter != null && UnderlyingSetter == null;
345 protected virtual bool LookupIsReadPublic ()
347 if (underlying_member == null)
349 if (UnderlyingGetter != null)
350 return UnderlyingGetter.IsPublic;
354 protected virtual bool LookupIsUnknown ()
356 return underlying_member == null;
359 protected virtual bool LookupIsWriteOnly ()
361 var pi = underlying_member as PropertyInfo;
363 return !pi.CanRead && pi.CanWrite;
364 return UnderlyingGetter == null && UnderlyingSetter != null;
367 protected virtual bool LookupIsWritePublic ()
369 if (underlying_member == null)
371 if (UnderlyingSetter != null)
372 return UnderlyingSetter.IsPublic;
376 protected virtual XamlType LookupTargetType ()
381 protected virtual XamlType LookupType ()
384 type = context.GetXamlType (DoGetType ());
391 var pi = underlying_member as PropertyInfo;
393 return pi.PropertyType;
394 var ei = underlying_member as EventInfo;
396 return ei.EventHandlerType;
397 if (underlying_setter != null)
398 return underlying_setter.GetParameters () [1].ParameterType;
399 if (underlying_getter != null)
400 return underlying_getter.GetParameters () [0].ParameterType;
401 return typeof (object);
404 protected virtual XamlValueConverter<TypeConverter> LookupTypeConverter ()
406 var t = Type.UnderlyingType;
409 if (t == typeof (object)) // it is different from XamlType.LookupTypeConverter().
412 var a = GetCustomAttributeProvider ();
413 var ca = a != null ? a.GetCustomAttribute<TypeConverterAttribute> (false) : null;
415 return context.GetValueConverter<TypeConverter> (System.Type.GetType (ca.ConverterTypeName), Type);
417 return Type.TypeConverter;
420 protected virtual MethodInfo LookupUnderlyingGetter ()
422 return underlying_getter;
425 protected virtual MemberInfo LookupUnderlyingMember ()
427 return underlying_member;
430 protected virtual MethodInfo LookupUnderlyingSetter ()
432 return underlying_setter;
435 protected virtual XamlValueConverter<ValueSerializer> LookupValueSerializer ()
437 if (is_predefined_directive) // FIXME: this is likely a hack.
442 return XamlType.LookupValueSerializer (Type, LookupCustomAttributeProvider ()) ?? Type.ValueSerializer;
445 void VerifyGetter (MethodInfo method)
449 if (method.GetParameters ().Length != 1 || method.ReturnType == typeof (void))
450 throw new ArgumentException (String.Format ("Property getter for {0} must have exactly one argument and must have non-void return type.", Name));
453 void VerifyAdderSetter (MethodInfo method)
457 if (method.GetParameters ().Length != 2)
458 throw new ArgumentException (String.Format ("Property getter or event adder for {0} must have exactly one argument and must have non-void return type.", Name));