2008-12-08 Atsushi Enomoto <atsushi@ximian.com>
[mono.git] / mcs / class / System.XML / System.Xml.Schema / XmlSchemaInference.cs
old mode 100755 (executable)
new mode 100644 (file)
index 76f1e0b..7d0ee9b
@@ -40,6 +40,7 @@ using Form = System.Xml.Schema.XmlSchemaForm;
 using Use = System.Xml.Schema.XmlSchemaUse;
 using SOMList = System.Xml.Schema.XmlSchemaObjectCollection;
 using SOMObject = System.Xml.Schema.XmlSchemaObject;
+using Import = System.Xml.Schema.XmlSchemaImport;
 using Element = System.Xml.Schema.XmlSchemaElement;
 using Attr = System.Xml.Schema.XmlSchemaAttribute;
 using AttrGroup = System.Xml.Schema.XmlSchemaAttributeGroup;
@@ -65,8 +66,14 @@ using Choice = System.Xml.Schema.XmlSchemaChoice;
 
 namespace System.Xml.Schema
 {
-       [MonoTODO ("merge primitive types; infer gYearMonth too; in some cases sequence should contain element whose minOccurs=0 (no obvious rules right now); reject some non-supported schema components")]
-       public class XmlSchemaInference
+       [MonoTODO]
+       // FIXME:
+       // - merge primitive types
+       // - infer gYearMonth too
+       // - in some cases sequence should contain element whose minOccurs=0
+       //    (no obvious rules right now)
+       // - reject some non-supported schema components
+       public sealed class XmlSchemaInference
        {
                public enum InferenceOption {
                        Restricted,
@@ -86,7 +93,7 @@ namespace System.Xml.Schema
                }
 
                public InferenceOption TypeInference {
-                       get { return TypeInference; }
+                       get { return typeInference; }
                        set { typeInference = value; }
                }
 
@@ -108,33 +115,78 @@ namespace System.Xml.Schema
        {
                public static XmlSchemaSet Process (XmlReader xmlReader, 
                        XmlSchemaSet schemas,
-                       bool laxOccurence,
+                       bool laxOccurrence,
                        bool laxTypeInference)
                {
                        XsdInference impl = new XsdInference (xmlReader,
-                               schemas, laxOccurence, laxTypeInference);
+                               schemas, laxOccurrence, laxTypeInference);
                        impl.Run ();
                        return impl.schemas;
                }
 
-               public const string NamespaceXml = "http://www.w3.org/XML/1998/namespace";
+               public const string NamespaceXml =
+                       "http://www.w3.org/XML/1998/namespace";
 
-               public const string NamespaceXmlns = "http://www.w3.org/2000/xmlns/";
+               public const string NamespaceXmlns =
+                       "http://www.w3.org/2000/xmlns/";
 
-               public const string XdtNamespace = "http://www.w3.org/2003/11/xpath-datatypes";
+               public const string XdtNamespace =
+                       "http://www.w3.org/2003/11/xpath-datatypes";
 
-               static readonly QName QNameString = new QName ("string",
-                       XmlSchema.Namespace);
+               static readonly QName QNameString = new QName (
+                       "string", XmlSchema.Namespace);
 
-               static readonly QName QNameBoolean = new QName ("boolean",
-                       XmlSchema.Namespace);
+               static readonly QName QNameBoolean = new QName (
+                       "boolean", XmlSchema.Namespace);
 
-               static readonly QName QNameAnyType = new QName ("anyType",
-                       XmlSchema.Namespace);
+               static readonly QName QNameAnyType = new QName (
+                       "anyType", XmlSchema.Namespace);
+
+               static readonly QName QNameByte = new QName (
+                       "byte", XmlSchema.Namespace);
+
+               static readonly QName QNameUByte = new QName (
+                       "unsignedByte", XmlSchema.Namespace);
+
+               static readonly QName QNameShort = new QName (
+                       "short", XmlSchema.Namespace);
+
+               static readonly QName QNameUShort = new QName (
+                       "unsignedShort", XmlSchema.Namespace);
+
+               static readonly QName QNameInt = new QName (
+                       "int", XmlSchema.Namespace);
+
+               static readonly QName QNameUInt = new QName (
+                       "unsignedInt", XmlSchema.Namespace);
+
+               static readonly QName QNameLong = new QName (
+                       "long", XmlSchema.Namespace);
+
+               static readonly QName QNameULong = new QName (
+                       "unsignedLong", XmlSchema.Namespace);
+
+               static readonly QName QNameDecimal = new QName (
+                       "decimal", XmlSchema.Namespace);
+
+               static readonly QName QNameUDecimal = new QName (
+                       "unsignedDecimal", XmlSchema.Namespace);
+
+               static readonly QName QNameDouble = new QName (
+                       "double", XmlSchema.Namespace);
+
+               static readonly QName QNameFloat = new QName (
+                       "float", XmlSchema.Namespace);
+
+               static readonly QName QNameDateTime = new QName (
+                       "dateTime", XmlSchema.Namespace);
+
+               static readonly QName QNameDuration = new QName (
+                       "duration", XmlSchema.Namespace);
 
                XmlReader source;
                XmlSchemaSet schemas;
-               bool laxOccurence;
+               bool laxOccurrence;
                bool laxTypeInference;
 
                Hashtable newElements = new Hashtable ();
@@ -142,12 +194,12 @@ namespace System.Xml.Schema
 
                private XsdInference (XmlReader xmlReader, 
                        XmlSchemaSet schemas, 
-                       bool laxOccurence, 
+                       bool laxOccurrence, 
                        bool laxTypeInference)
                {
                        this.source = xmlReader;
                        this.schemas = schemas;
-                       this.laxOccurence = laxOccurence;
+                       this.laxOccurrence = laxOccurrence;
                        this.laxTypeInference = laxTypeInference;
                }
 
@@ -158,7 +210,6 @@ namespace System.Xml.Schema
 
                        // move to top-level element
                        source.MoveToContent ();
-                       int depth = source.Depth;
                        if (source.NodeType != XmlNodeType.Element)
                                throw new ArgumentException ("Argument XmlReader content is expected to be an element.");
 
@@ -171,6 +222,28 @@ namespace System.Xml.Schema
                        }
                        else
                                InferElement (el, qname.Namespace, false);
+
+                       // FIXME: compile again.
+//                     foreach (XmlSchema schema in schemas.Schemas ())
+//                             schemas.Reprocess (schema);
+               }
+
+               private void AddImport (string current, string import)
+               {
+                       foreach (XmlSchema schema in schemas.Schemas (current)) {
+                               bool exists = false;
+                               foreach (XmlSchemaExternal e in schema.Includes) {
+                                       Import imp = e as Import;
+                                       if (imp != null &&
+                                               imp.Namespace == import)
+                                               exists = true;
+                               }
+                               if (exists)
+                                       continue;
+                               Import newimp = new Import ();
+                               newimp.Namespace = import;
+                               schema.Includes.Add (newimp);
+                       }
                }
 
                private void IncludeXmlAttributes ()
@@ -269,7 +342,7 @@ namespace System.Xml.Schema
                                Attr attr = table [attrName] as Attr;
                                if (attr == null) {
                                        attList.Add (InferNewAttribute (
-                                               attrName, isNew));
+                                               attrName, isNew, ns));
                                } else {
                                        table.Remove (attrName);
                                        if (attr.RefName != null &&
@@ -287,7 +360,7 @@ namespace System.Xml.Schema
                }
 
                private XmlSchemaAttribute InferNewAttribute (
-                       QName attrName, bool isNewTypeDefinition)
+                       QName attrName, bool isNewTypeDefinition, string ns)
                {
                        Attr attr = null;
                        bool mergedRequired = false;
@@ -305,6 +378,7 @@ namespace System.Xml.Schema
                                }
                                attr = new Attr ();
                                attr.RefName = attrName;
+                               AddImport (ns, attrName.Namespace);
                        } else {
                                // local attribute
                                attr = new Attr ();
@@ -312,7 +386,7 @@ namespace System.Xml.Schema
                                attr.SchemaTypeName =
                                        InferSimpleType (source.Value);
                        }
-                       if (!laxOccurence &&
+                       if (!laxOccurrence &&
                                (isNewTypeDefinition || mergedRequired))
                                attr.Use = Use.Required;
                        else
@@ -339,17 +413,18 @@ namespace System.Xml.Schema
                                typeName);
                        if (st == null) // non-primitive type => see above.
                                return QNameString;
-                       try {
-                               st.Datatype.ParseValue (value,
-                                       source.NameTable,
-                                       source as IXmlNamespaceResolver);
-                               // the string value was value
-                               return typeName;
-                       } catch {
-                               // The types were incompatible.
-                               // FIXME: find the base common type
-                               return QNameString;
-                       }
+                       do {
+                               try {
+                                       st.Datatype.ParseValue (value,
+                                               source.NameTable,
+                                               source as IXmlNamespaceResolver);
+                                       return typeName;
+                               } catch {
+                                       st = st.BaseXmlSchemaType as XmlSchemaSimpleType;
+                                       typeName = st != null ? st.QualifiedName : QName.Empty;
+                               }
+                       } while (typeName != QName.Empty);
+                       return QNameString;
                }
 
                private SOMList GetAttributes (ComplexType ct)
@@ -731,9 +806,11 @@ namespace System.Xml.Schema
                        Element nel = new Element ();
                        if (source.NamespaceURI == ns)
                                nel.Name = source.LocalName;
-                       else
+                       else {
                                nel.RefName = new QName (source.LocalName,
                                        source.NamespaceURI);
+                               AddImport (ns, source.NamespaceURI);
+                       }
                        InferElement (nel, source.NamespaceURI, true);
                        c.Items.Add (nel);
                }
@@ -773,12 +850,17 @@ namespace System.Xml.Schema
                                QName name = new QName (source.LocalName,
                                        source.NamespaceURI);
                                Element nel = CreateElement (name);
+                               if (laxOccurrence)
+                                       nel.MinOccurs = 0;
                                InferElement (nel, ns, true);
                                if (ns == name.Namespace)
                                        s.Items.Add (nel);
                                else {
                                        Element re = new Element ();
+                                       if (laxOccurrence)
+                                               re.MinOccurs = 0;
                                        re.RefName = name;
+                                       AddImport (ns, name.Namespace);
                                        s.Items.Add (re);
                                }
                                consumed = true;
@@ -839,7 +921,7 @@ namespace System.Xml.Schema
                private Choice ToSequenceOfChoice (Sequence s)
                {
                        Choice c = new Choice ();
-                       if (laxOccurence)
+                       if (laxOccurrence)
                                c.MinOccurs = 0;
                        c.MaxOccursString = "unbounded";
                        foreach (Particle p in s.Items)
@@ -878,7 +960,7 @@ namespace System.Xml.Schema
                private Sequence CreateSequence ()
                {
                        Sequence s = new Sequence ();
-                       if (laxOccurence)
+                       if (laxOccurrence)
                                s.MinOccurs = 0;
                        return s;
                }
@@ -920,7 +1002,7 @@ namespace System.Xml.Schema
                                return QNameString;
 
                        switch (value) {
-                       // 0 and 1 are not infered as byte unlike MS.XSDInfer
+                       // 0 and 1 are not inferred as byte unlike MS.XSDInfer
 //                     case "0":
 //                     case "1":
                        case "true":
@@ -930,49 +1012,49 @@ namespace System.Xml.Schema
                        try {
                                long dec = XmlConvert.ToInt64 (value);
                                if (byte.MinValue <= dec && dec <= byte.MaxValue)
-                                       return XmlSchemaType.GetBuiltInSimpleType (XmlTypeCode.UnsignedByte).QualifiedName;
+                                       return QNameUByte;
                                if (sbyte.MinValue <= dec && dec <= sbyte.MaxValue)
-                                       return XmlSchemaType.GetBuiltInSimpleType (XmlTypeCode.Byte).QualifiedName;
+                                       return QNameByte;
                                if (ushort.MinValue <= dec && dec <= ushort.MaxValue)
-                                       return XmlSchemaType.GetBuiltInSimpleType (XmlTypeCode.UnsignedShort).QualifiedName;
+                                       return QNameUShort;
                                if (short.MinValue <= dec && dec <= short.MaxValue)
-                                       return XmlSchemaType.GetBuiltInSimpleType (XmlTypeCode.Short).QualifiedName;
+                                       return QNameShort;
                                if (uint.MinValue <= dec && dec <= uint.MaxValue)
-                                       return XmlSchemaType.GetBuiltInSimpleType (XmlTypeCode.UnsignedInt).QualifiedName;
+                                       return QNameUInt;
                                if (int.MinValue <= dec && dec <= int.MaxValue)
-                                       return XmlSchemaType.GetBuiltInSimpleType (XmlTypeCode.Int).QualifiedName;
-                               return XmlSchemaType.GetBuiltInSimpleType (XmlTypeCode.Long).QualifiedName;
+                                       return QNameInt;
+                               return QNameLong;
                        } catch (Exception) {
                        }
                        try {
                                XmlConvert.ToUInt64 (value);
-                               return XmlSchemaType.GetBuiltInSimpleType (XmlTypeCode.UnsignedLong).QualifiedName;
+                               return QNameULong;
                        } catch (Exception) {
                        }
                        try {
                                XmlConvert.ToDecimal (value);
-                               return XmlSchemaType.GetBuiltInSimpleType (XmlTypeCode.Decimal).QualifiedName;
+                               return QNameDecimal;
                        } catch (Exception) {
                        }
                        try {
                                double dbl = XmlConvert.ToDouble (value);
                                if (float.MinValue <= dbl &&
                                        dbl <= float.MaxValue)
-                                       return XmlSchemaType.GetBuiltInSimpleType (XmlTypeCode.Float).QualifiedName;
+                                       return QNameFloat;
                                else
-                                       return XmlSchemaType.GetBuiltInSimpleType (XmlTypeCode.Double).QualifiedName;
+                                       return QNameDouble;
                        } catch (Exception) {
                        }
                        try {
                                // FIXME: also try DateTimeSerializationMode
                                // and gYearMonth
                                XmlConvert.ToDateTime (value);
-                               return XmlSchemaType.GetBuiltInSimpleType (XmlTypeCode.DateTime).QualifiedName;
+                               return QNameDateTime;
                        } catch (Exception) {
                        }
                        try {
                                XmlConvert.ToTimeSpan (value);
-                               return XmlSchemaType.GetBuiltInSimpleType (XmlTypeCode.Duration).QualifiedName;
+                               return QNameDuration;
                        } catch (Exception) {
                        }