Merge pull request #1275 from ranma42/fix-lib64
[mono.git] / mcs / class / System.XML / System.Xml.Serialization / TypeData.cs
index 4bffcecc981759415c8fee4dde441413971c27be..861fcc11de67455023df3b2fbe422a78319b02ec 100644 (file)
@@ -4,6 +4,7 @@
 // Authors:
 //     Gonzalo Paniagua Javier (gonzalo@ximian.com)
 //  Lluis Sanchez Gual (lluis@ximian.com)
+//  Atsushi Enomoto (atsushi@ximian.com)
 //
 // (C) 2002 Ximian, Inc (http://www.ximian.com)
 //
 
 using System;
 using System.Collections;
+#if NET_2_0
+using System.Collections.Generic;
+#endif
 using System.Globalization;
 using System.Reflection;
+using System.Text;
 using System.Xml.Schema;
 
 namespace System.Xml.Serialization
 {
+       [AttributeUsage (AttributeTargets.Class, AllowMultiple = false)]
+       internal class XmlTypeConvertorAttribute : Attribute
+       {
+               /*
+                * Bug #12571:
+                * 
+                * System.Xml.Linq.XElement should be deserializable from an XmlElement.
+                * 
+                * Types can now register a custom deserializer by adding this custom attribute.
+                * Method is the name of a private 'static method (static object)' method that will
+                * be invoked to construct an instance of the object.
+                * 
+                */
+               public string Method {
+                       get;
+                       private set;
+               }
+
+               public XmlTypeConvertorAttribute (string method)
+               {
+                       Method = method;
+               }
+       }
+
        internal class TypeData
        {
                Type type;
@@ -45,17 +74,25 @@ namespace System.Xml.Serialization
                Type listItemType;
                string typeName;
                string fullTypeName;
+               string csharpName;
+               string csharpFullName;
                TypeData listItemTypeData;
                TypeData listTypeData;
                TypeData mappedType;
                XmlSchemaPatternFacet facet;
+               MethodInfo typeConvertor;
                bool hasPublicConstructor = true;
+               bool nullableOverride;
 
                public TypeData (Type type, string elementName, bool isPrimitive) :
                        this(type, elementName, isPrimitive, null, null) {}
 
                public TypeData (Type type, string elementName, bool isPrimitive, TypeData mappedType, XmlSchemaPatternFacet facet)
                {
+#if NET_2_0
+                       if (type.IsGenericTypeDefinition)
+                               throw new InvalidOperationException ("Generic type definition cannot be used in serialization. Only specific generic types can be used.");
+#endif
                        this.mappedType = mappedType;
                        this.facet = facet;
                        this.type = type;
@@ -86,6 +123,8 @@ namespace System.Xml.Serialization
                        if (sType == SchemaTypes.Array || sType == SchemaTypes.Class) {
                                hasPublicConstructor = !type.IsInterface && (type.IsArray || type.GetConstructor (Type.EmptyTypes) != null || type.IsAbstract || type.IsValueType);
                        }
+
+                       LookupTypeConvertor ();
                }
 
                internal TypeData (string typeName, string fullTypeName, string xmlType, SchemaTypes schemaType, TypeData listItemTypeData)
@@ -98,6 +137,23 @@ namespace System.Xml.Serialization
                        this.hasPublicConstructor = true;
                }
 
+               void LookupTypeConvertor ()
+               {
+#if NET_4_5
+                       // We only need this for System.Xml.Linq.
+                       var convertor = type.GetCustomAttribute<XmlTypeConvertorAttribute> ();
+                       if (convertor != null)
+                               typeConvertor = type.GetMethod (convertor.Method, BindingFlags.Static | BindingFlags.NonPublic);
+#endif
+               }
+
+               internal void ConvertForAssignment (ref object value)
+               {
+                       // Has this object registered a custom type converter?
+                       if (typeConvertor != null)
+                               value = typeConvertor.Invoke (null, new object[] { value });
+               }
+
                public string TypeName
                {
                        get {
@@ -126,6 +182,97 @@ namespace System.Xml.Serialization
                        }
                }
 
+               public string CSharpName
+               {
+                       get {
+                               if (csharpName == null)
+                                       csharpName = (Type == null) ? TypeName : ToCSharpName (Type, false);
+                               return csharpName;
+                       }
+               }
+
+               public string CSharpFullName
+               {
+                       get {
+                               if (csharpFullName == null)
+                                       csharpFullName = (Type == null) ? TypeName : ToCSharpName (Type, true);
+                               return csharpFullName;
+                       }
+               }
+
+               // static Microsoft.CSharp.CSharpCodeProvider csprovider =
+               //      new Microsoft.CSharp.CSharpCodeProvider ();
+
+               public static string ToCSharpName (Type type, bool full)
+               {
+                       // return csprovider.GetTypeOutput (new System.CodeDom.CodeTypeReference (type));
+                       StringBuilder sb = new StringBuilder ();
+                       
+                       if (type.IsArray) {
+                               sb.Append (ToCSharpName (type.GetElementType (), full));
+                               sb.Append ('[');
+                               int rank = type.GetArrayRank ();
+                               for (int i = 1; i < rank; i++)
+                                       sb.Append (',');
+                               sb.Append (']');
+                               return sb.ToString ();
+                       }
+#if NET_2_0
+                       // Generic nested types return the complete list of type arguments,
+                       // including type arguments for the declaring class. This requires
+                       // some special handling
+                       if (type.IsGenericType && !type.IsGenericTypeDefinition) {
+                               Type[] args = type.GetGenericArguments ();
+                               int nt = args.Length - 1;
+                               Type pt = type;
+                               // Loop throguh the declaring class chain, consuming type arguments for every
+                               // generic class in the chain
+                               while (pt != null) {
+                                       if (sb.Length > 0)
+                                               sb.Insert (0, '.');
+                                       int i = pt.Name.IndexOf ('`');
+                                       if (i != -1) {
+                                               int na = nt - int.Parse (pt.Name.Substring (i+1));
+                                               sb.Insert (0,'>');
+                                               for (;nt > na; nt--) {
+                                                       sb.Insert (0, ToCSharpName (args[nt],full));
+                                                       if (nt - 1 != na)
+                                                               sb.Insert (0, ',');
+                                               }
+                                               sb.Insert (0,'<');
+                                               sb.Insert (0, pt.Name.Substring (0, i));
+                                       } else
+                                               sb.Insert (0, pt.Name);
+                                       pt = pt.DeclaringType;
+                               }
+                               if (full && type.Namespace.Length > 0)
+                                       sb.Insert (0, type.Namespace + ".");
+                               return sb.ToString ();
+                       }
+#endif
+                       if (type.DeclaringType != null) {
+                               sb.Append (ToCSharpName (type.DeclaringType, full)).Append ('.');
+                               sb.Append (type.Name);
+                       }
+                       else {
+                               if (full && type.Namespace.Length > 0)
+                                       sb.Append (type.Namespace).Append ('.');
+                               sb.Append (type.Name);
+                       }
+                       return sb.ToString ();
+               }
+               
+               static bool IsKeyword (string name)
+               {
+                       if (keywordsTable == null) {
+                               Hashtable t = new Hashtable ();
+                               foreach (string s in keywords)
+                                       t [s] = s;
+                               keywordsTable = t;
+                       }
+                       return keywordsTable.Contains (name);
+               }
+
                public SchemaTypes SchemaType
                {
                        get {
@@ -160,6 +307,33 @@ namespace System.Xml.Serialization
                        }
                }
 
+               public bool NullableOverride
+               {
+                       get { return nullableOverride; }
+               }
+
+               public bool IsNullable
+               {
+                       get
+                       {
+                               if (nullableOverride)
+                                       return true;
+#if NET_2_0
+                               return !IsValueType ||
+                                       (type != null &&
+                                        type.IsGenericType &&
+                                        type.GetGenericTypeDefinition () == typeof (Nullable<>));
+#else
+                               return !IsValueType;
+#endif
+                       }
+
+                       set
+                       {
+                               nullableOverride = value;
+                       }
+               }
+
                public TypeData ListItemTypeData
                {
                        get
@@ -179,22 +353,31 @@ namespace System.Xml.Serialization
 
                                if (listItemType != null) return listItemType;
 
+                               Type genericArgument = null;
+
                                if (SchemaType != SchemaTypes.Array)
                                        throw new InvalidOperationException (Type.FullName + " is not a collection");
                                else if (type.IsArray) 
                                        listItemType = type.GetElementType ();
-                               else if (typeof(ICollection).IsAssignableFrom (type))
+#if NET_2_0
+                               else if (typeof (ICollection).IsAssignableFrom (type) || (genericArgument = GetGenericListItemType (type)) != null)
+#else
+                               else if (typeof (ICollection).IsAssignableFrom (type))
+#endif
                                {
                                        if (typeof (IDictionary).IsAssignableFrom (type))
                                                throw new NotSupportedException (string.Format (CultureInfo.InvariantCulture,
                                                        "The type {0} is not supported because it implements" +
                                                        " IDictionary.", type.FullName));
 
-                                       PropertyInfo prop = GetIndexerProperty (type);
-                                       if (prop == null) 
-                                               throw new InvalidOperationException ("You must implement a default accessor on " + type.FullName + " because it inherits from ICollection");
-
-                                       listItemType = prop.PropertyType;
+                                       if (genericArgument != null)
+                                               listItemType = genericArgument;
+                                       else {
+                                               PropertyInfo prop = GetIndexerProperty (type);
+                                               if (prop == null) 
+                                                       throw new InvalidOperationException ("You must implement a default accessor on " + type.FullName + " because it inherits from ICollection");
+                                               listItemType = prop.PropertyType;
+                                       }
 
                                        MethodInfo addMethod = type.GetMethod ("Add", new Type[] { listItemType });
                                        if (addMethod == null)
@@ -283,5 +466,39 @@ namespace System.Xml.Serialization
                                "hierarchy. {2} does not implement Add({1}).", inheritFrom, 
                                argumentType.FullName, type.FullName));
                }
+
+               private static Hashtable keywordsTable;
+               private static string[] keywords = new string[] {
+                       "abstract","event","new","struct","as","explicit","null","switch","base","extern",
+                       "this","false","operator","throw","break","finally","out","true",
+                       "fixed","override","try","case","params","typeof","catch","for",
+                       "private","foreach","protected","checked","goto","public",
+                       "unchecked","class","if","readonly","unsafe","const","implicit","ref",
+                       "continue","in","return","using","virtual","default",
+                       "interface","sealed","volatile","delegate","internal","do","is",
+                       "sizeof","while","lock","stackalloc","else","static","enum",
+                       "namespace",
+                       "object","bool","byte","float","uint","char","ulong","ushort",
+                       "decimal","int","sbyte","short","double","long","string","void",
+#if NET_2_0
+                       "partial", "yield", "where"
+#endif
+               };
+
+#if NET_2_0
+               internal static Type GetGenericListItemType (Type type)
+               {
+                       if (type.IsGenericType && typeof(IEnumerable).IsAssignableFrom(type.GetGenericTypeDefinition ())) {
+                               Type [] gatypes = type.GetGenericArguments ();
+                               if (type.GetMethod ("Add", gatypes) != null)
+                                       return gatypes [0];
+                       }
+                       Type t = null;
+                       foreach (Type i in type.GetInterfaces ())
+                               if ((t = GetGenericListItemType (i)) != null)
+                                       return t;
+                       return null;
+               }
+#endif
        }
 }