2007-02-19 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.XML / System.Xml.Serialization / TypeData.cs
index 6ddd3ab798ea3226d701080553b41675e3156bca..5e3853e591f5f4d3ccc4b09cdb08678614ef9114 100644 (file)
@@ -4,13 +4,38 @@
 // 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)
 //
 
+//
+// Permission is hereby granted, free of charge, to any person obtaining
+// a copy of this software and associated documentation files (the
+// "Software"), to deal in the Software without restriction, including
+// without limitation the rights to use, copy, modify, merge, publish,
+// distribute, sublicense, and/or sell copies of the Software, and to
+// permit persons to whom the Software is furnished to do so, subject to
+// the following conditions:
+// 
+// The above copyright notice and this permission notice shall be
+// included in all copies or substantial portions of the Software.
+// 
+// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
+// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
+// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
+// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
+// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
+// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+//
+
 using System;
 using System.Collections;
+using System.Globalization;
 using System.Reflection;
+using System.Text;
+using System.Xml.Schema;
 
 namespace System.Xml.Serialization
 {
@@ -20,11 +45,31 @@ namespace System.Xml.Serialization
                string elementName;
                SchemaTypes sType;
                Type listItemType;
+               string typeName;
+               string fullTypeName;
+               string csharpName;
+               string csharpFullName;
+               TypeData listItemTypeData;
+               TypeData listTypeData;
+               TypeData mappedType;
+               XmlSchemaPatternFacet facet;
+               bool hasPublicConstructor = true;
+               bool nullableOverride;
 
-               public TypeData (Type type, string elementName, bool isPrimitive)
+               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;
-                       this.elementName = elementName;
+                       this.typeName = type.Name;
+                       this.fullTypeName = type.FullName.Replace ('+', '.');
 
                        if (isPrimitive)
                                sType = SchemaTypes.Primitive;
@@ -41,12 +86,31 @@ namespace System.Xml.Serialization
                                else
                                        sType = SchemaTypes.Class;
                        }
+                       
+                       if (IsListType)
+                               this.elementName = TypeTranslator.GetArrayName (ListItemTypeData.XmlType);
+                       else
+                               this.elementName = elementName;
+
+                       if (sType == SchemaTypes.Array || sType == SchemaTypes.Class) {
+                               hasPublicConstructor = !type.IsInterface && (type.IsArray || type.GetConstructor (Type.EmptyTypes) != null || type.IsAbstract || type.IsValueType);
+                       }
+               }
+
+               internal TypeData (string typeName, string fullTypeName, string xmlType, SchemaTypes schemaType, TypeData listItemTypeData)
+               {
+                       this.elementName = xmlType;
+                       this.typeName = typeName;
+                       this.fullTypeName = fullTypeName.Replace ('+', '.');
+                       this.listItemTypeData = listItemTypeData;
+                       this.sType = schemaType;
+                       this.hasPublicConstructor = true;
                }
 
                public string TypeName
                {
                        get {
-                               return type.Name;
+                               return typeName;
                        }
                }
                                
@@ -67,11 +131,62 @@ namespace System.Xml.Serialization
                public string FullTypeName
                {
                        get {
-//                             return type.FullName.Replace ('+', '.');
-                               return type.FullName;
+                               return fullTypeName;
+                       }
+               }
+
+               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));
+                       if (type.IsArray) {
+                               StringBuilder sb = new StringBuilder ();
+                               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
+                       if (type.IsGenericType && !type.IsGenericTypeDefinition) {
+                               StringBuilder sb = new StringBuilder ();
+                               sb.Append (ToCSharpName (type.GetGenericTypeDefinition (), full));
+                               sb.Append ('<');
+                               foreach (Type arg in type.GetGenericArguments ())
+                                       sb.Append (ToCSharpName (arg, full)).Append (',');
+                               sb.Length--;
+                               sb.Append ('>');
+                               return sb.ToString ();
+                       }
+#endif
+                       string name = full ? type.FullName : type.Name;
+                       name = name.Replace ('+', '.');
+                       int idx = name.IndexOf ('`'); // generic definition has extra `n.
+                       return idx > 0 ? name.Substring (0, idx) : name;
+               }
+
                public SchemaTypes SchemaType
                {
                        get {
@@ -90,14 +205,61 @@ namespace System.Xml.Serialization
                        { 
                                return (SchemaType == SchemaTypes.Class || 
                                              SchemaType == SchemaTypes.Array ||
-                                             SchemaType == SchemaTypes.Enum ); 
+                                             SchemaType == SchemaTypes.Enum ||
+                                             SchemaType == SchemaTypes.XmlNode ||
+                                                 SchemaType == SchemaTypes.XmlSerializable ||
+                                                 !IsXsdType); 
+                       }
+               }
+
+               public bool IsValueType
+               {
+                       get
+                       {
+                               if (type != null) return type.IsValueType;
+                               else return (sType == SchemaTypes.Primitive || sType == SchemaTypes.Enum);
+                       }
+               }
+
+               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 = true;
                        }
                }
 
+               public TypeData ListItemTypeData
+               {
+                       get
+                       {
+                               if (listItemTypeData == null && type != null)
+                                       listItemTypeData = TypeTranslator.GetTypeData (ListItemType);
+                               return listItemTypeData;
+                       }
+               }
+               
                public Type ListItemType
                {
                        get
                        {
+                               if (type == null) 
+                                       throw new InvalidOperationException ("Property ListItemType is not supported for custom types");
+
                                if (listItemType != null) return listItemType;
 
                                if (SchemaType != SchemaTypes.Array)
@@ -106,17 +268,85 @@ namespace System.Xml.Serialization
                                        listItemType = type.GetElementType ();
                                else if (typeof(ICollection).IsAssignableFrom (type))
                                {
+                                       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");
-                                       return prop.PropertyType;
+                                       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)
+                                               throw CreateMissingAddMethodException (type, "ICollection",
+                                                       listItemType);
+                               }
+                               else // at this point, we must be dealing with IEnumerable implementation
+                               {
+                                       MethodInfo met = type.GetMethod ("GetEnumerator", Type.EmptyTypes);
+                                       if (met == null) { 
+                                               // get private implemenation
+                                               met = type.GetMethod ("System.Collections.IEnumerable.GetEnumerator",
+                                                       BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance,
+                                                       null, Type.EmptyTypes, null);
+                                       }
+                                       // determine ListItemType using IEnumerator.Current property
+                                       PropertyInfo prop = met.ReturnType.GetProperty ("Current");
+                                       if (prop == null)
+                                               listItemType = typeof (object);
+                                       else
+                                               listItemType = prop.PropertyType;
+
+                                       MethodInfo addMethod = type.GetMethod ("Add", new Type[] { listItemType });
+                                       if (addMethod == null)
+                                               throw CreateMissingAddMethodException (type, "IEnumerable",
+                                                       listItemType);
                                }
-                               else
-                                       return type.GetMethod ("Add").GetParameters()[0].ParameterType;
 
                                return listItemType;
                        }
                }
 
+               public TypeData ListTypeData
+               {
+                       get
+                       {
+                               if (listTypeData != null) return listTypeData;
+                               
+                               listTypeData = new TypeData (TypeName + "[]",
+                                       FullTypeName + "[]",
+                                       TypeTranslator.GetArrayName(XmlType),
+                                       SchemaTypes.Array, this);
+
+                               return listTypeData;
+                       }
+               }
+
+               public bool IsXsdType {
+                       get { return mappedType == null; }
+               }
+
+               public TypeData MappedType {
+                       get {
+                               return mappedType != null ? mappedType : this;
+                       }
+               }
+
+               public XmlSchemaPatternFacet XmlSchemaPatternFacet {
+                       get {
+                               return facet;
+                       }
+               }
+               
+               public bool HasPublicConstructor
+               {
+                       get { return hasPublicConstructor; }
+               }
+
+
                public static PropertyInfo GetIndexerProperty (Type collectionType)
                {
                        PropertyInfo[] props = collectionType.GetProperties (BindingFlags.Instance | BindingFlags.Public);
@@ -128,6 +358,13 @@ namespace System.Xml.Serialization
                        }
                        return null;
                }
+
+               private static InvalidOperationException CreateMissingAddMethodException (Type type, string inheritFrom, Type argumentType) {
+                       return new InvalidOperationException (string.Format(CultureInfo.InvariantCulture,
+                               "To be XML serializable, types which inherit from {0} must have " +
+                               "an implementation of Add({1}) at all levels of their inheritance " +
+                               "hierarchy. {2} does not implement Add({1}).", inheritFrom, 
+                               argumentType.FullName, type.FullName));
+               }
        }
 }
-