New test.
[mono.git] / mcs / class / System.XML / System.Xml.Serialization / XmlReflectionImporter.cs
index 32e4603db4a5aeb15b8642b884cc255013445949..3dce3f49832a278a63c753f34630ee9ac5f3105d 100644 (file)
@@ -106,46 +106,65 @@ namespace System.Xml.Serialization {
                        XmlReflectionMember [] members,
                        bool hasWrapperElement)
                {
-//                     Reset ();       Disabled. See ChangeLog
-
-                       XmlMemberMapping[] mapping = new XmlMemberMapping[members.Length];
-                       for (int n=0; n<members.Length; n++)
-                       {
-                               XmlTypeMapMember mapMem = CreateMapMember (null, members[n], ns);
-                               mapping[n] = new XmlMemberMapping (members[n].MemberName, ns, mapMem, false);
-                       }
-                       elementName = XmlConvert.EncodeLocalName (elementName);
-                       XmlMembersMapping mps = new XmlMembersMapping (elementName, ns, hasWrapperElement, false, mapping);
-                       mps.RelatedMaps = relatedMaps;
-                       mps.Format = SerializationFormat.Literal;
-                       Type[] extraTypes = includedTypes != null ? (Type[])includedTypes.ToArray(typeof(Type)) : null;
-                       mps.Source = new MembersSerializationSource (elementName, hasWrapperElement, members, false, true, ns, extraTypes);
-                       if (allowPrivateTypes) mps.Source.CanBeGenerated = false;
-                       return mps;
+                       return ImportMembersMapping (elementName, ns, members, hasWrapperElement, true);
                }
 
 #if NET_2_0
                [MonoTODO]
-               public XmlMembersMapping ImportMembersMapping (string elementName, 
+               public
+#endif
+               XmlMembersMapping ImportMembersMapping (string elementName, 
                        string ns, 
                        XmlReflectionMember[] members, 
                        bool hasWrapperElement, 
-                       bool rpc)
+                       bool writeAccessors)
                {
-                       throw new NotImplementedException ();
+                       return ImportMembersMapping (elementName, ns, members, hasWrapperElement, writeAccessors, true);
                }
 
+#if NET_2_0
                [MonoTODO]
-               public XmlMembersMapping ImportMembersMapping (string elementName, 
+               public
+#endif
+               XmlMembersMapping ImportMembersMapping (string elementName, 
                        string ns, 
                        XmlReflectionMember[] members, 
                        bool hasWrapperElement, 
-                       bool rpc
-                       bool openModel)
+                       bool writeAccessors
+                       bool validate)
                {
-                       throw new NotImplementedException ();
+                       return ImportMembersMapping (elementName, ns, members, hasWrapperElement, writeAccessors, validate, XmlMappingAccess.Read | XmlMappingAccess.Write);
                }
+
+#if NET_2_0
+               [MonoTODO] // FIXME: handle writeAccessors, validate, and mapping access
+               public
 #endif
+               XmlMembersMapping ImportMembersMapping (string elementName, 
+                       string ns, 
+                       XmlReflectionMember[] members, 
+                       bool hasWrapperElement, 
+                       bool writeAccessors, 
+                       bool validate,
+                       XmlMappingAccess access)
+               {
+//                     Reset ();       Disabled. See ChangeLog
+
+                       XmlMemberMapping[] mapping = new XmlMemberMapping[members.Length];
+                       for (int n=0; n<members.Length; n++)
+                       {
+                               XmlTypeMapMember mapMem = CreateMapMember (null, members[n], ns);
+                               mapping[n] = new XmlMemberMapping (members[n].MemberName, ns, mapMem, false);
+                       }
+                       elementName = XmlConvert.EncodeLocalName (elementName);
+                       XmlMembersMapping mps = new XmlMembersMapping (elementName, ns, hasWrapperElement, false, mapping);
+                       mps.RelatedMaps = relatedMaps;
+                       mps.Format = SerializationFormat.Literal;
+                       Type[] extraTypes = includedTypes != null ? (Type[])includedTypes.ToArray(typeof(Type)) : null;
+                       mps.Source = new MembersSerializationSource (elementName, hasWrapperElement, members, false, true, ns, extraTypes);
+                       if (allowPrivateTypes) mps.Source.CanBeGenerated = false;
+                       return mps;
+               }
 
                public XmlTypeMapping ImportTypeMapping (Type type)
                {
@@ -168,30 +187,52 @@ namespace System.Xml.Serialization {
                                throw new ArgumentNullException ("type");
 
                        if (type == typeof (void))
-                               throw new InvalidOperationException ("Type " + type.Name + " may not be serialized.");
+                               throw new NotSupportedException ("The type " + type.FullName + " may not be serialized.");
+
+                       return ImportTypeMapping (TypeTranslator.GetTypeData (type), root, 
+                               defaultNamespace);
+               }
+
+               internal XmlTypeMapping ImportTypeMapping (TypeData typeData, string defaultNamespace)
+               {
+                       return ImportTypeMapping (typeData, (XmlRootAttribute) null, 
+                               defaultNamespace);
+               }
+
+               private XmlTypeMapping ImportTypeMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
+               {
+                       if (typeData == null)
+                               throw new ArgumentNullException ("typeData");
+
+                       if (typeData.Type == null)
+                               throw new ArgumentException ("Specified TypeData instance does not have Type set.");
 
                        if (defaultNamespace == null) defaultNamespace = initialDefaultNamespace;
                        if (defaultNamespace == null) defaultNamespace = string.Empty;
 
-                       XmlTypeMapping map;
+                       try {
+                               XmlTypeMapping map;
+
+                               switch (typeData.SchemaType) {
+                                       case SchemaTypes.Class: map = ImportClassMapping (typeData, root, defaultNamespace); break;
+                                       case SchemaTypes.Array: map = ImportListMapping (typeData, root, defaultNamespace, null, 0); break;
+                                       case SchemaTypes.XmlNode: map = ImportXmlNodeMapping (typeData, root, defaultNamespace); break;
+                                       case SchemaTypes.Primitive: map = ImportPrimitiveMapping (typeData, root, defaultNamespace); break;
+                                       case SchemaTypes.Enum: map = ImportEnumMapping (typeData, root, defaultNamespace); break;
+                                       case SchemaTypes.XmlSerializable: map = ImportXmlSerializableMapping (typeData, root, defaultNamespace); break;
+                                       default: throw new NotSupportedException ("Type " + typeData.Type.FullName + " not supported for XML stialization");
+                               }
 
-                       switch (TypeTranslator.GetTypeData(type).SchemaType)
-                       {
-                               case SchemaTypes.Class: map = ImportClassMapping (type, root, defaultNamespace); break;
-                               case SchemaTypes.Array: map = ImportListMapping (type, root, defaultNamespace, null, 0); break;
-                               case SchemaTypes.XmlNode: map = ImportXmlNodeMapping (type, root, defaultNamespace); break;
-                               case SchemaTypes.Primitive: map = ImportPrimitiveMapping (type, root, defaultNamespace); break;
-                               case SchemaTypes.Enum: map = ImportEnumMapping (type, root, defaultNamespace); break;
-                               case SchemaTypes.XmlSerializable: map = ImportXmlSerializableMapping (type, root, defaultNamespace); break;
-                               default: throw new NotSupportedException ("Type " + type.FullName + " not supported for XML stialization");
+                               map.RelatedMaps = relatedMaps;
+                               map.Format = SerializationFormat.Literal;
+                               Type[] extraTypes = includedTypes != null ? (Type[]) includedTypes.ToArray (typeof (Type)) : null;
+                               map.Source = new XmlTypeSerializationSource (typeData.Type, root, attributeOverrides, defaultNamespace, extraTypes);
+                               if (allowPrivateTypes) map.Source.CanBeGenerated = false;
+                               return map;
+                       } catch (InvalidOperationException ex) {
+                               throw new InvalidOperationException (string.Format (CultureInfo.InvariantCulture,
+                                       "There was an error reflecting type '{0}'.", typeData.Type.FullName), ex);
                        }
-
-                       map.RelatedMaps = relatedMaps;
-                       map.Format = SerializationFormat.Literal;
-                       Type[] extraTypes = includedTypes != null ? (Type[])includedTypes.ToArray(typeof(Type)) : null;
-                       map.Source = new XmlTypeSerializationSource (type, root, attributeOverrides, defaultNamespace, extraTypes);
-                       if (allowPrivateTypes) map.Source.CanBeGenerated = false;
-                       return map;
                }
 
                XmlTypeMapping CreateTypeMapping (TypeData typeData, XmlRootAttribute root, string defaultXmlType, string defaultNamespace)
@@ -201,7 +242,7 @@ namespace System.Xml.Serialization {
                        string elementName;
                        bool includeInSchema = true;
                        XmlAttributes atts = null;
-                       bool nullable = true;
+                       bool nullable = CanBeNull (typeData);
 
                        if (defaultXmlType == null) defaultXmlType = typeData.XmlType;
 
@@ -246,11 +287,23 @@ namespace System.Xml.Serialization {
                        if (typeNamespace == null) typeNamespace = rootNamespace;
                        
                        XmlTypeMapping map;
-                       if (typeData.SchemaType == SchemaTypes.XmlSerializable)
-                               map = new XmlSerializableMapping (elementName, rootNamespace, typeData, defaultXmlType, typeNamespace);
-                       else
-                               map = new XmlTypeMapping (elementName, rootNamespace, typeData, defaultXmlType, typeNamespace);
-                               
+                       switch (typeData.SchemaType) {
+                               case SchemaTypes.XmlSerializable:
+                                       map = new XmlSerializableMapping (elementName, rootNamespace, typeData, defaultXmlType, typeNamespace);
+                                       break;
+                               case SchemaTypes.Primitive:
+                                       if (!typeData.IsXsdType)
+                                               map = new XmlTypeMapping (elementName, rootNamespace, 
+                                                       typeData, defaultXmlType, XmlSerializer.WsdlTypesNamespace);
+                                       else
+                                               map = new XmlTypeMapping (elementName, rootNamespace, 
+                                                       typeData, defaultXmlType, typeNamespace);
+                                       break;
+                               default:
+                                       map = new XmlTypeMapping (elementName, rootNamespace, typeData, defaultXmlType, typeNamespace);
+                                       break;
+                       }
+
                        map.IncludeInSchema = includeInSchema;
                        map.IsNullable = nullable;
                        relatedMaps.Add (map);
@@ -261,6 +314,13 @@ namespace System.Xml.Serialization {
                XmlTypeMapping ImportClassMapping (Type type, XmlRootAttribute root, string defaultNamespace)
                {
                        TypeData typeData = TypeTranslator.GetTypeData (type);
+                       return ImportClassMapping (typeData, root, defaultNamespace);
+               }
+
+               XmlTypeMapping ImportClassMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
+               {
+                       Type type = typeData.Type;
+
                        XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
                        if (map != null) return map;
 
@@ -276,26 +336,26 @@ namespace System.Xml.Serialization {
                        ClassMap classMap = new ClassMap ();
                        map.ObjectMap = classMap;
 
-//                     try
-//                     {
-                               ICollection members = GetReflectionMembers (type);
-                               foreach (XmlReflectionMember rmember in members)
-                               {
-                                       string ns = map.XmlTypeNamespace;
-                                       if (rmember.XmlAttributes.XmlIgnore) continue;
-                                       if (rmember.DeclaringType != null && rmember.DeclaringType != type) {
-                                               XmlTypeMapping bmap = ImportClassMapping (rmember.DeclaringType, root, defaultNamespace);
-                                               ns = bmap.XmlTypeNamespace;
-                                       }
-                                       
+                       ICollection members = GetReflectionMembers (type);
+                       foreach (XmlReflectionMember rmember in members)
+                       {
+                               string ns = map.XmlTypeNamespace;
+                               if (rmember.XmlAttributes.XmlIgnore) continue;
+                               if (rmember.DeclaringType != null && rmember.DeclaringType != type) {
+                                       XmlTypeMapping bmap = ImportClassMapping (rmember.DeclaringType, root, defaultNamespace);
+                                       ns = bmap.XmlTypeNamespace;
+                               }
+
+                               try {
                                        XmlTypeMapMember mem = CreateMapMember (type, rmember, ns);
                                        mem.CheckOptionalValueType (type);
                                        classMap.AddMember (mem);
+                               } catch (Exception ex) {
+                                       throw new InvalidOperationException (string.Format (
+                                               CultureInfo.InvariantCulture, "There was an error" +
+                                               " reflecting field '{0}'.", rmember.MemberName), ex);
                                }
-//                     }
-//                     catch (Exception ex) {
-//                             throw helper.CreateError (map, ex.Message);
-//                     }
+                       }
 
                        // Import extra classes
 
@@ -394,6 +454,12 @@ namespace System.Xml.Serialization {
                XmlTypeMapping ImportListMapping (Type type, XmlRootAttribute root, string defaultNamespace, XmlAttributes atts, int nestingLevel)
                {
                        TypeData typeData = TypeTranslator.GetTypeData (type);
+                       return ImportListMapping (typeData, root, defaultNamespace, atts, nestingLevel);
+               }
+
+               XmlTypeMapping ImportListMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace, XmlAttributes atts, int nestingLevel)
+               {
+                       Type type = typeData.Type;
                        ListMap obmap = new ListMap ();
 
                        if (!allowPrivateTypes)
@@ -505,12 +571,13 @@ namespace System.Xml.Serialization {
                        return map;
                }
 
-               XmlTypeMapping ImportXmlNodeMapping (Type type, XmlRootAttribute root, string defaultNamespace)
+               XmlTypeMapping ImportXmlNodeMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
                {
-                       XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (TypeTranslator.GetTypeData (type), root, defaultNamespace));
+                       Type type = typeData.Type;
+                       XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
                        if (map != null) return map;
 
-                       map = CreateTypeMapping (TypeTranslator.GetTypeData (type), root, null, defaultNamespace);
+                       map = CreateTypeMapping (typeData, root, null, defaultNamespace);
                        helper.RegisterClrType (map, type, map.XmlTypeNamespace);
                        
                        if (type.BaseType != null)
@@ -525,9 +592,9 @@ namespace System.Xml.Serialization {
                        return map;
                }
 
-               XmlTypeMapping ImportPrimitiveMapping (Type type, XmlRootAttribute root, string defaultNamespace)
+               XmlTypeMapping ImportPrimitiveMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
                {
-                       TypeData typeData = TypeTranslator.GetTypeData (type);
+                       Type type = typeData.Type;
                        XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
                        if (map != null) return map;
                        map = CreateTypeMapping (typeData, root, null, defaultNamespace);
@@ -535,9 +602,9 @@ namespace System.Xml.Serialization {
                        return map;
                }
 
-               XmlTypeMapping ImportEnumMapping (Type type, XmlRootAttribute root, string defaultNamespace)
+               XmlTypeMapping ImportEnumMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
                {
-                       TypeData typeData = TypeTranslator.GetTypeData (type);
+                       Type type = typeData.Type;
                        XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
                        if (map != null) return map;
                        
@@ -545,31 +612,33 @@ namespace System.Xml.Serialization {
                                ReflectionHelper.CheckSerializableType (type, false);
                                
                        map = CreateTypeMapping (typeData, root, null, defaultNamespace);
+                       map.IsNullable = false;
                        helper.RegisterClrType (map, type, map.XmlTypeNamespace);
 
                        string [] names = Enum.GetNames (type);
                        ArrayList members = new ArrayList();
                        foreach (string name in names)
                        {
-                               MemberInfo[] mem = type.GetMember (name);
+                               FieldInfo field = type.GetField (name);
                                string xmlName = null;
-                               object[] atts = mem[0].GetCustomAttributes (typeof(XmlIgnoreAttribute), false);
-                               if (atts.Length > 0) continue;
-                               atts = mem[0].GetCustomAttributes (typeof(XmlEnumAttribute), false);
+                               if (field.IsDefined(typeof(XmlIgnoreAttribute), false))
+                                       continue;
+                               object[] atts = field.GetCustomAttributes (typeof(XmlEnumAttribute), false);
                                if (atts.Length > 0) xmlName = ((XmlEnumAttribute)atts[0]).Name;
                                if (xmlName == null) xmlName = name;
-                               members.Add (new EnumMap.EnumMapMember (xmlName, name));
+                               long value = ((IConvertible) field.GetValue (null)).ToInt64 (CultureInfo.InvariantCulture);
+                               members.Add (new EnumMap.EnumMapMember (xmlName, name, value));
                        }
 
-                       bool isFlags = type.GetCustomAttributes (typeof(FlagsAttribute),false).Length > 0;
+                       bool isFlags = type.IsDefined (typeof (FlagsAttribute), false);
                        map.ObjectMap = new EnumMap ((EnumMap.EnumMapMember[])members.ToArray (typeof(EnumMap.EnumMapMember)), isFlags);
                        ImportTypeMapping (typeof(object)).DerivedTypes.Add (map);
                        return map;
                }
 
-               XmlTypeMapping ImportXmlSerializableMapping (Type type, XmlRootAttribute root, string defaultNamespace)
+               XmlTypeMapping ImportXmlSerializableMapping (TypeData typeData, XmlRootAttribute root, string defaultNamespace)
                {
-                       TypeData typeData = TypeTranslator.GetTypeData (type);
+                       Type type = typeData.Type;
                        XmlTypeMapping map = helper.GetRegisteredClrType (type, GetTypeNamespace (typeData, root, defaultNamespace));
                        if (map != null) return map;
                        
@@ -823,7 +892,7 @@ namespace System.Xml.Serialization {
                                mapMember = member;
                        }
 
-                       mapMember.DefaultValue = atts.XmlDefaultValue;
+                       mapMember.DefaultValue = GetDefaultValue (typeData, atts.XmlDefaultValue);
                        mapMember.TypeData = typeData;
                        mapMember.Name = rmember.MemberName;
                        mapMember.IsReturnValue = rmember.IsReturnValue;
@@ -881,11 +950,12 @@ namespace System.Xml.Serialization {
                        {
                                Type elemType = (att.Type != null) ? att.Type : defaultType;
                                XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(elemType, att.DataType));
-                               elem.Namespace = (att.Namespace != null) ? att.Namespace : defaultNamespace;
                                elem.Form = att.Form;
+                               if (elem.Form != XmlSchemaForm.Unqualified)
+                                       elem.Namespace = (att.Namespace != null) ? att.Namespace : defaultNamespace;
                                elem.IsNullable = att.IsNullable;
-                               
-                               if (elem.IsNullable && elem.TypeData.IsValueType)
+
+                               if (elem.IsNullable && !elem.TypeData.IsNullable)
                                        throw new InvalidOperationException ("IsNullable may not be 'true' for value type " + elem.TypeData.FullTypeName + " in member '" + defaultName + "'");
                                        
                                if (elem.TypeData.IsComplexType)
@@ -912,8 +982,13 @@ namespace System.Xml.Serialization {
                                }
 
                                if (choiceEnumMap != null) {
-                                       string cname = choiceEnumMap.GetEnumName (elem.ElementName);
-                                       if (cname == null) throw new InvalidOperationException ("The '" + choiceEnumType + "' enumeration does not have a value for the element '" + elem.ElementName + "'");
+                                       string cname = choiceEnumMap.GetEnumName (choiceEnumType.FullName, elem.ElementName);
+                                       if (cname == null)
+                                               throw new InvalidOperationException (string.Format (
+                                                       CultureInfo.InvariantCulture, "Type {0} is missing"
+                                                       + " enumeration value '{1}' for element '{1} from"
+                                                       + " namespace '{2}'.", choiceEnumType, elem.ElementName,
+                                                       elem.Namespace));
                                        elem.ChoiceValue = Enum.Parse (choiceEnumType, cname);
                                }
                                        
@@ -953,7 +1028,13 @@ namespace System.Xml.Serialization {
                        if (atts.XmlText != null)
                        {
                                member.IsXmlTextCollector = true;
-                               if (atts.XmlText.Type != null) defaultType = atts.XmlText.Type;
+                               if (atts.XmlText.Type != null) {
+                                       TypeData td = TypeTranslator.GetTypeData (defaultType);
+                                       if ((td.SchemaType == SchemaTypes.Primitive || td.SchemaType == SchemaTypes.Enum) && atts.XmlText.Type != defaultType) {
+                                               throw new InvalidOperationException ("The type for XmlText may not be specified for primitive types.");
+                                       }
+                                       defaultType = atts.XmlText.Type;
+                               }
                                if (defaultType == typeof(XmlNode)) defaultType = typeof(XmlText);      // Nodes must be text nodes
 
                                XmlTypeMapElementInfo elem = new XmlTypeMapElementInfo (member, TypeTranslator.GetTypeData(defaultType, atts.XmlText.DataType));
@@ -973,7 +1054,11 @@ namespace System.Xml.Serialization {
                
                bool CanBeNull (TypeData type)
                {
-                       return (type.SchemaType != SchemaTypes.Primitive || type.Type == typeof (string));
+#if !NET_2_0   // idiotic compatibility
+                       if (type.Type == typeof (XmlQualifiedName))
+                               return false;
+#endif
+                       return !type.Type.IsValueType;
                }
                
                public void IncludeType (Type type)
@@ -1001,6 +1086,32 @@ namespace System.Xml.Serialization {
                                IncludeType (at.Type);
                }
 
+               private object GetDefaultValue (TypeData typeData, object defaultValue)
+               {
+                       if (defaultValue == DBNull.Value || typeData.SchemaType != SchemaTypes.Enum)
+                               return defaultValue;
+
+                       // get string representation of enum value
+                       string namedValue = Enum.Format (typeData.Type, defaultValue, "g");
+                       // get decimal representation of enum value
+                       string decimalValue = Enum.Format (typeData.Type, defaultValue, "d");
+
+                       // if decimal representation matches string representation, then
+                       // the value is not defined in the enum type (as the "g" format
+                       // will return the decimal equivalent of the value if the value
+                       // is not equal to a combination of named enumerated constants
+                       if (namedValue == decimalValue) {
+                               string msg = string.Format (CultureInfo.InvariantCulture,
+                                       "Value '{0}' cannot be converted to {1}.", defaultValue,
+                                       defaultValue.GetType ().FullName);
+                               throw new InvalidOperationException (msg);
+                       }
+
+                       // XmlSerializer expects integral enum value
+                       //return namedValue.Replace (',', ' ');
+                       return defaultValue;
+               }
+
                #endregion // Methods
        }
 }