context: using System;
  using System.ComponentModel;
  using System.Collections;
  using System.Reflection;
  using System.Xml;
  
  namespace Stetic 
  {
  	public class TypedPropertyDescriptor : PropertyDescriptor {
  
  		PropertyInfo memberInfo, propertyInfo, runtimePropertyInfo, runtimeMemberInfo;
  		ParamSpec pspec;
  		TypedPropertyDescriptor gladeProperty;
  		bool isWrapperProperty;
  		TypedClassDescriptor klass;
  		bool defaultSet;
  
  		const BindingFlags flags = BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance;
  
  		public TypedPropertyDescriptor (XmlElement elem, ItemGroup group, TypedClassDescriptor klass) : base (elem, group, klass)
  		{
  			this.klass = klass;
  			string propertyName = elem.GetAttribute ("name");
  			int dot = propertyName.IndexOf ('.');
  
  			if (dot != -1) {
  				// Sub-property (eg, "Alignment.Value")
  				memberInfo = FindProperty (klass.WrapperType, klass.WrappedType, propertyName.Substring (0, dot));
  				isWrapperProperty = memberInfo.DeclaringType.IsSubclassOf (typeof (ObjectWrapper));
  				gladeProperty = new TypedPropertyDescriptor (isWrapperProperty ? klass.WrapperType : klass.WrappedType, memberInfo.Name);
  				propertyInfo = FindProperty (memberInfo.PropertyType, propertyName.Substring (dot + 1));
  			} else {
  				// Basic simple property
  				propertyInfo = FindProperty (klass.WrapperType, klass.WrappedType, propertyName);
  				isWrapperProperty = propertyInfo.DeclaringType.IsSubclassOf (typeof (ObjectWrapper));
  			}
  			
  			// Wrapper properties that override widgets properties (using the same name)
  			// must be considered runtime properties (will be available at run-time).
  			if (!isWrapperProperty || klass.WrappedType.GetProperty (propertyName) != null)
  				isRuntimeProperty = true;
  			
  			if (!IsInternal && propertyInfo.PropertyType.IsEnum &&
  			    Registry.LookupEnum (propertyInfo.PropertyType.FullName) == null)
  				throw new ArgumentException ("No EnumDescriptor for " + propertyInfo.PropertyType.FullName + "(" + klass.WrappedType.FullName + "." + propertyName + ")");
  
  			pspec = FindPSpec (propertyInfo);
  			
  			if (isWrapperProperty && pspec == null) {
  				PropertyInfo pinfo = klass.WrappedType.GetProperty (propertyInfo.Name, flags);
  				if (pinfo != null)
  					pspec = FindPSpec (pinfo);
  			}
  
  			if (pspec != null) {
  				// This information will be overridden by what's specified in the xml file
  				description = pspec.Blurb;
  				minimum = pspec.Minimum;
  				maximum = pspec.Maximum;
  				label = propertyName;
  				if (!elem.HasAttribute ("ignore-default"))
  					hasDefault = Type.GetTypeCode (PropertyType) != TypeCode.Object || PropertyType.IsEnum;
  			} else {
  				label = propertyInfo.Name;
  				gladeOverride = true;
  			}
  
  			string typeName = elem.GetAttribute ("editor");
  			if (typeName.Length > 0)
  				editorType = Registry.GetType (typeName, false);
  			
  			// Look for a default value attribute
  			
  			object[] ats = propertyInfo.GetCustomAttributes (typeof(DefaultValueAttribute), true);
  			if (ats.Length > 0) {
  				DefaultValueAttribute at = (DefaultValueAttribute) ats [0];
  				defaultValue = at.Value;
  			}
  			
  			// Load default data
  			Load (elem);
  		}
  
  		TypedPropertyDescriptor (Type objectType, string propertyName)
  		{
  			propertyInfo = FindProperty (objectType, propertyName);
  			isWrapperProperty = false;
  
  			pspec = FindPSpec (propertyInfo);
  			if (pspec != null) {
  				label = propertyName;
  				description = pspec.Blurb;
  				minimum = pspec.Minimum;
  				maximum = pspec.Maximum;
  				hasDefault = Type.GetTypeCode (PropertyType) != TypeCode.Object || PropertyType.IsEnum;
  			} else
  				label = propertyInfo.Name;
  		}
  
  		static PropertyInfo FindProperty (Type type, string propertyName) {
  			return FindProperty (null, type, propertyName);
  		}
  
  		static PropertyInfo FindProperty (Type wrapperType, Type objectType, string propertyName)
  		{
  			PropertyInfo info = null;
  
  			if (wrapperType != null) {
  				info = wrapperType.GetProperty (propertyName, flags);
  				if (info != null)
  					return info;
  			}
  
  			try {
  				info = objectType.GetProperty (propertyName, flags);
  			}
  			catch (AmbiguousMatchException) {
  				foreach (PropertyInfo pi in objectType.GetProperties ()) {
  					if (pi.Name == propertyName) {
  						info = pi;
  						break;
  					}
  				}
  			}
  
  			if (info != null)
  				return info;
  
  			throw new ArgumentException ("Invalid property name " + objectType.Name + "." + propertyName);
  		}
  
  		ParamSpec FindPSpec (PropertyInfo pinfo)
  		{
  			foreach (object attr in pinfo.GetCustomAttributes (false)) {
  				if (attr is GLib.PropertyAttribute) {
  					GLib.PropertyAttribute pattr = (GLib.PropertyAttribute)attr;
  					return ParamSpec.LookupObjectProperty (pinfo.DeclaringType, pattr.Name);
  				}
  
  				if (attr is Gtk.ChildPropertyAttribute) {
  					Gtk.ChildPropertyAttribute cpattr = (Gtk.ChildPropertyAttribute)attr;
  					return ParamSpec.LookupChildProperty (pinfo.DeclaringType.DeclaringType, cpattr.Name);
  				}
  			}
  			return null;
  		}
  
  		// The property's internal name
  		public override string Name {
  			get {
  				return propertyInfo.Name;
  			}
  		}
  
  		// The property's type
  		public override Type PropertyType {
  			get {
  				return propertyInfo.PropertyType;
  			}
  		}
  
  		// The property's PropertyInfo
  		public PropertyInfo PropertyInfo {
  			get {
  				return propertyInfo;
  			}
  		}
  
  		// The property's ParamSpec
  		public virtual ParamSpec ParamSpec {
  			get {
  				return pspec;
  			}
  		}
  
  		public override bool IsDefaultValue (object value)
  		{
  			if (defaultValue != null)
  				return base.IsDefaultValue (value);
  			if (ParamSpec != null && value != null)
  				return ParamSpec.IsDefaultValue (value);
  			else
  				return false;
  		}
  
  		public override void ResetValue (object instance) 
  		{
  			// This is a hack because there is no managed way of getting
  			// the default value of a GObject property. The call to LoadDefaultValues
  			// will guess the default values from a dummy instance
  			if (!defaultSet) {
  				ObjectWrapper ww = ObjectWrapper.Lookup (instance);
  				TypedClassDescriptor td = ww.ClassDescriptor as TypedClassDescriptor;
  				if (td != null)
  					td.LoadDefaultValues ();
  				defaultSet = true;
  			}
  			base.ResetValue (instance);
  		}
  		
  		internal void SetDefault (object val)
  		{
  			defaultValue = val;
  			defaultSet = true;
  		}
  		
  		// Gets the value of the property on @obj
  		public override object GetValue (object obj)
  		{
  			try {
  				if (isWrapperProperty)
  					obj = ObjectWrapper.Lookup (obj);
  				if (memberInfo != null)
  
gt: 					obj = memberInfo.GetValue (obj, null);