Merge pull request #495 from nicolas-raoul/fix-for-issue2907-with-no-formatting-changes
[mono.git] / mcs / class / System.XML / System.Xml.Serialization / XmlTypeMapping.cs
index d0e35adfc4875172fcc6e693d0b93ca41a546230..2b0f8bd829d2768ed76993523618639fae3e1f0c 100644 (file)
@@ -34,6 +34,7 @@ using System;
 using System.Collections;
 using System.Globalization;
 using System.Xml.Schema;
+using System.Reflection;
 
 namespace System.Xml.Serialization
 {
@@ -101,11 +102,18 @@ namespace System.Xml.Serialization
                internal string XmlType
                {
                        get { return xmlType; }
+                       set { xmlType = value; }
                }
 
                internal string XmlTypeNamespace
                {
-                       get { return xmlTypeNamespace; }
+                       get { return xmlTypeNamespace ?? string.Empty; }
+                       set { xmlTypeNamespace = value; }
+               }
+
+               internal bool HasXmlTypeNamespace
+               {
+                       get { return xmlTypeNamespace != null; }
                }
 
                internal ArrayList DerivedTypes
@@ -150,17 +158,16 @@ namespace System.Xml.Serialization
                        set { isNullable = value; }
                }
 
-               internal XmlTypeMapping GetRealTypeMap (string objectFullTypeName)
+               internal XmlTypeMapping GetRealTypeMap (Type objectType)
                {
                        if (TypeData.SchemaType == SchemaTypes.Enum)
                                return this;
 
                        // Returns the map for a subtype of this map's type
-                       objectFullTypeName = objectFullTypeName.Replace ('+','.');
-                       if (TypeFullName == objectFullTypeName) return this;
+                       if (TypeData.Type == objectType) return this;
                        for (int n=0; n<_derivedTypes.Count; n++) {
                                XmlTypeMapping map = (XmlTypeMapping) _derivedTypes[n];
-                               if (map.TypeFullName == objectFullTypeName) return map;
+                               if (map.TypeData.Type == objectType) return map;
                        }
                        
                        return null;
@@ -168,9 +175,9 @@ namespace System.Xml.Serialization
 
                internal XmlTypeMapping GetRealElementMap (string name, string ens)
                {
-                       if (xmlType == name && xmlTypeNamespace == ens) return this;
+                       if (xmlType == name && XmlTypeNamespace == ens) return this;
                        foreach (XmlTypeMapping map in _derivedTypes)
-                               if (map.xmlType == name && map.xmlTypeNamespace == ens) return map;
+                               if (map.xmlType == name && map.XmlTypeNamespace == ens) return map;
                        
                        return null;
                }
@@ -189,23 +196,95 @@ namespace System.Xml.Serialization
        internal class XmlSerializableMapping : XmlTypeMapping
        {
                XmlSchema _schema;
+#if NET_2_0 && !MOONLIGHT
+               XmlSchemaComplexType _schemaType;
+               XmlQualifiedName _schemaTypeName;
+#endif
 
-               internal XmlSerializableMapping(string elementName, string ns, TypeData typeData, string xmlType, string xmlTypeNamespace)
+               internal XmlSerializableMapping(XmlRootAttribute root, string elementName, string ns, TypeData typeData, string xmlType, string xmlTypeNamespace)
                        : base(elementName, ns, typeData, xmlType, xmlTypeNamespace)
                {
-                       IXmlSerializable serializable = (IXmlSerializable)Activator.CreateInstance(typeData.Type);
+#if NET_2_0 && !MOONLIGHT
+                       XmlSchemaProviderAttribute schemaProvider = (XmlSchemaProviderAttribute) Attribute.GetCustomAttribute (typeData.Type, typeof (XmlSchemaProviderAttribute));
+
+                       if (schemaProvider != null) {
+                               string method = schemaProvider.MethodName;
+                               MethodInfo mi = typeData.Type.GetMethod (method, BindingFlags.Static | BindingFlags.Public | BindingFlags.FlattenHierarchy);
+                               if (mi == null)
+                                       throw new InvalidOperationException (String.Format ("Type '{0}' must implement public static method '{1}'", typeData.Type, method));
+                               if (!typeof (XmlQualifiedName).IsAssignableFrom (mi.ReturnType) &&
+                                   // LAMESPEC: it is undocumented. (We don't have to tell users about it in the error message.)
+                                   // Also do not add such a silly compatibility test to assert that it does not raise an error.
+                                   !typeof (XmlSchemaComplexType).IsAssignableFrom (mi.ReturnType))
+                                       throw new InvalidOperationException (String.Format ("Method '{0}' indicated by XmlSchemaProviderAttribute must have its return type as XmlQualifiedName", method));
+                               XmlSchemaSet xs = new XmlSchemaSet ();
+                               object retVal = mi.Invoke (null, new object [] { xs });
+                               _schemaTypeName = XmlQualifiedName.Empty;
+                               if (retVal == null)
+                                       return;
+
+                               if (retVal is XmlSchemaComplexType) {
+                                       _schemaType = (XmlSchemaComplexType) retVal;
+                                       if (!_schemaType.QualifiedName.IsEmpty)
+                                               _schemaTypeName = _schemaType.QualifiedName;
+                                       else
+                                               _schemaTypeName = new XmlQualifiedName (xmlType, xmlTypeNamespace);
+                               }
+                               else if (retVal is XmlQualifiedName) {
+                                       _schemaTypeName = (XmlQualifiedName)retVal;
+                               }
+                               else
+                                       throw new InvalidOperationException (
+                                               String.Format ("Method {0}.{1}() specified by XmlSchemaProviderAttribute has invalid signature: return type must be compatible with System.Xml.XmlQualifiedName.", typeData.Type.Name, method));
+
+                               // defaultNamespace at XmlReflectionImporter takes precedence for Namespace, but not for XsdTypeNamespace.
+                               UpdateRoot (new XmlQualifiedName (root != null ? root.ElementName : _schemaTypeName.Name, root != null ? root.Namespace : Namespace ?? _schemaTypeName.Namespace));
+                               XmlTypeNamespace = _schemaTypeName.Namespace;
+                               XmlType = _schemaTypeName.Name;
+
+                               if (!_schemaTypeName.IsEmpty && xs.Count > 0) {
+                                       XmlSchema [] schemas = new XmlSchema [xs.Count];
+                                       xs.CopyTo (schemas, 0);
+                                       _schema = schemas [0];
+                               }
+
+                               return;
+                       }
+#endif
+#if NET_2_0 && !MOONLIGHT
+                       IXmlSerializable serializable = (IXmlSerializable)Activator.CreateInstance (typeData.Type, true);
+                       try {
+                               _schema = serializable.GetSchema();
+                       } catch (Exception) {
+                               // LAMESPEC: .NET has a bad exception catch and swallows it silently.
+                       }
+#else
+                       IXmlSerializable serializable = (IXmlSerializable)Activator.CreateInstance (typeData.Type);
                        _schema = serializable.GetSchema();
+#endif
+#if !MOONLIGHT
                        if (_schema != null) 
                        {
                                if (_schema.Id == null || _schema.Id.Length == 0) 
                                        throw new InvalidOperationException("Schema Id is missing. The schema returned from " + typeData.Type.FullName + ".GetSchema() must have an Id.");
                        }
+#endif
                }
 
                internal XmlSchema Schema
                {
                        get { return _schema; }
                }
+
+#if NET_2_0 && !MOONLIGHT
+               internal XmlSchemaType SchemaType {
+                       get { return _schemaType; }
+               }
+
+               internal XmlQualifiedName SchemaTypeName {
+                       get { return _schemaTypeName; }
+               }
+#endif
        }
  
 
@@ -229,10 +308,14 @@ namespace System.Xml.Serialization
                XmlTypeMapMember _returnMember;
                bool _ignoreMemberNamespace;
                bool _canBeSimpleType = true;
+               bool? _isOrderDependentMap;
 
                public void AddMember (XmlTypeMapMember member)
                {
-                       member.GlobalIndex = _allMembers.Count;
+                       // If GlobalIndex has not been set, set it now
+                       if (member.GlobalIndex == -1)
+                               member.GlobalIndex = _allMembers.Count;
+                       
                        _allMembers.Add (member);
                        
                        if (!(member.DefaultValue is System.DBNull) && member.DefaultValue != null) {
@@ -247,7 +330,7 @@ namespace System.Xml.Serialization
                        {
                                XmlTypeMapMemberAttribute atm = (XmlTypeMapMemberAttribute)member;
                                if (_attributeMembers == null) _attributeMembers = new Hashtable();
-                               string key = BuildKey (atm.AttributeName, atm.Namespace);
+                               string key = BuildKey (atm.AttributeName, atm.Namespace, -1);
                                if (_attributeMembers.ContainsKey (key))
                                        throw new InvalidOperationException ("The XML attribute named '" + atm.AttributeName + "' from namespace '" + atm.Namespace + "' is already present in the current scope. Use XML attributes to specify another XML name or namespace for the attribute.");
                                member.Index = _attributeMembers.Count;
@@ -292,7 +375,7 @@ namespace System.Xml.Serialization
                        ICollection elemsInfo = ((XmlTypeMapMemberElement)member).ElementInfo;
                        foreach (XmlTypeMapElementInfo elem in elemsInfo)
                        {
-                               string key = BuildKey (elem.ElementName, elem.Namespace);
+                               string key = BuildKey (elem.ElementName, elem.Namespace, elem.ExplicitOrder);
                                if (_elements.ContainsKey (key)) 
                                        throw new InvalidOperationException ("The XML element named '" + elem.ElementName + "' from namespace '" + elem.Namespace + "' is already present in the current scope. Use XML attributes to specify another XML name or namespace for the element.");
                                _elements.Add (key, elem);
@@ -314,13 +397,24 @@ namespace System.Xml.Serialization
                public XmlTypeMapMemberAttribute GetAttribute (string name, string ns)
                {
                        if (_attributeMembers == null) return null;
-                       return (XmlTypeMapMemberAttribute)_attributeMembers [BuildKey(name,ns)];
+                       return (XmlTypeMapMemberAttribute)_attributeMembers [BuildKey (name,ns, -1)];
+               }
+
+               public XmlTypeMapElementInfo GetElement (string name, string ns, int order)
+               {
+                       if (_elements == null) return null;
+                       return (XmlTypeMapElementInfo)_elements [BuildKey (name,ns, order)];
                }
 
-               public XmlTypeMapElementInfo GetElement (string name, string ns)
+               public XmlTypeMapElementInfo GetElement(string name, string ns)
                {
                        if (_elements == null) return null;
-                       return (XmlTypeMapElementInfo)_elements [BuildKey(name,ns)];
+
+                       foreach (XmlTypeMapElementInfo info in _elements.Values)
+                               if (info.ElementName == name && info.Namespace == ns)
+                                       return info;
+
+                       return null;
                }
                
                public XmlTypeMapElementInfo GetElement (int index)
@@ -338,14 +432,15 @@ namespace System.Xml.Serialization
                                        _elementsByIndex [mem.Index] = (XmlTypeMapElementInfo) mem.ElementInfo [0];
                                }
                        }
-                       
+                       if (index >= _elementMembers.Count)
+                               return null;
                        return _elementsByIndex [index];
                }
                
-               private string BuildKey (string name, string ns)
+               private string BuildKey (string name, string ns, int explicitOrder)
                {
                        if (_ignoreMemberNamespace) return name;
-                       else return name + " / " + ns;
+                       else return name + " / " + ns + (explicitOrder < 0 ? "" : "/" + explicitOrder);
                }
                
                public ICollection AllElementInfos
@@ -360,6 +455,20 @@ namespace System.Xml.Serialization
                        set { _ignoreMemberNamespace = value; }
                }
 
+               public bool IsOrderDependentMap {
+                       get {
+                               if (_isOrderDependentMap == null) {
+                                       _isOrderDependentMap = false;
+                                       foreach (XmlTypeMapElementInfo ei in _elements.Values)
+                                               if (ei.ExplicitOrder >= 0) {
+                                                       _isOrderDependentMap = true;
+                                                       break;
+                                               }
+                               }
+                               return (bool) _isOrderDependentMap;
+                       }
+               }
+
                public XmlTypeMapMember FindMember (string name)
                {
                        for (int n=0; n<_allMembers.Count; n++)