2003-03-20 Duncan Mak <duncan@ximian.com>
authorDuncan Mak <duncan@mono-cvs.ximian.com>
Thu, 20 Mar 2003 05:50:50 +0000 (05:50 -0000)
committerDuncan Mak <duncan@mono-cvs.ximian.com>
Thu, 20 Mar 2003 05:50:50 +0000 (05:50 -0000)
* MonoXSD.cs: More refinements. We now support transforming
enumerations as well as arrays to their corresponding XML Schema
type. Read/Write properties will also be transformed (thanks to
reading KeithBa's book). XmlNode is properly handled, and XmlElement
& co. should also be transformed correctly.

Next up: I should look into whether or not xsd.exe makes use of
XmlAttributes. If so, I'll have to support that.

Other than that, this part of MonoXSD should be complete.

svn path=/trunk/mcs/; revision=12695

mcs/tools/mono-xsd/ChangeLog
mcs/tools/mono-xsd/MonoXSD.cs

index cf1bea36c042800b86eba8efec896c0d4dfd031b..6e7a8c04e194ecaf760c13c4d3fc9e9b87ba9401 100644 (file)
@@ -1,3 +1,16 @@
+2003-03-20  Duncan Mak  <duncan@ximian.com>
+
+       * MonoXSD.cs: More refinements. We now support transforming
+       enumerations as well as arrays to their corresponding XML Schema
+       type. Read/Write properties will also be transformed (thanks to
+       reading KeithBa's book). XmlNode is properly handled, and XmlElement
+       & co. should also be transformed correctly.
+       
+       Next up: I should look into whether or not xsd.exe makes use of
+       XmlAttributes. If so, I'll have to support that.
+       
+       Other than that, this part of MonoXSD should be complete.
+
 2003-03-17  Duncan Mak  <duncan@ximian.com>
 
        * MonoXSD.cs: A simple implementation of MonoXSD. It current
index 6f02765e68cc3a0870668f34dad8f09440883d18..996ee1a75249957429d28c447ae232c7b49cba5b 100755 (executable)
@@ -18,18 +18,24 @@ using System.Xml.Schema;
 namespace Mono.Util {\r
         class MonoXSD {\r
 \r
-                static BindingFlags instance_flag = BindingFlags.Public | BindingFlags.DeclaredOnly | BindingFlags.Instance;\r
+                static BindingFlags flags = BindingFlags.Public | BindingFlags.Instance | BindingFlags.DeclaredOnly;\r
                 static XmlSchema schema = new XmlSchema ();\r
-               static readonly string xs = "http://www.w3.org/2001/XMLSchema";\r
+                static readonly string xs = "http://www.w3.org/2001/XMLSchema";\r
                 static Hashtable generatedSchemaTypes;\r
 \r
                 static void Main (string [] args) \r
                 {\r
                         string assembly = args [0];\r
 \r
-                        if (assembly.EndsWith (".dll") || assembly.EndsWith (".exe")) {\r
-                                GenerateSchema (assembly);\r
-                        } else {\r
+                        if (assembly.EndsWith (".dll") || assembly.EndsWith (".exe"))\r
+                                try {\r
+                                        WriteSchema (assembly);\r
+                                } catch (ArgumentException e) {\r
+                                        Console.WriteLine (e.Message + "\n");\r
+                                        Environment.Exit (0);\r
+                                }\r
+                        \r
+                        else {\r
                                 Console.WriteLine ("Not supported.");\r
                                 return;\r
                         }\r
@@ -38,7 +44,7 @@ namespace Mono.Util {
                 /// <summary>\r
                 ///     Writes a schema for each type in the assembly\r
                 /// </summary>\r
-                static void GenerateSchema (string assembly) \r
+                static void WriteSchema (string assembly) \r
                 {\r
                         Assembly a = Assembly.LoadFrom (assembly);\r
                         generatedSchemaTypes = new Hashtable ();\r
@@ -46,13 +52,24 @@ namespace Mono.Util {
                         if (a == null)\r
                                 throw new NullReferenceException ("Null assembly");\r
 \r
+                        XmlSchemaType schemaType;\r
+\r
                         foreach (Type t in a.GetTypes ()) {\r
-                                XmlSchemaType schemaType = GenerateSchemaType (t);\r
-                                XmlSchemaElement schemaElement = GenerateSchemaElement (t, schemaType);\r
+                                try {\r
+                                        schemaType = WriteSchemaType (t);\r
+                                } catch (ArgumentException e) {\r
+                                        throw new ArgumentException (String.Format ("Error: We cannot process {0}\n{1}", assembly, e.Message));\r
+                                }\r
+\r
+                                if (schemaType == null)\r
+                                        continue;\r
+\r
+                                XmlSchemaElement schemaElement = WriteSchemaElement (t, schemaType);\r
                                 schema.Items.Add (schemaElement);\r
                                 schema.Items.Add (schemaType);\r
                         }\r
 \r
+                        schema.ElementFormDefault = XmlSchemaForm.Qualified;\r
                         schema.Compile (new ValidationEventHandler (OnSchemaValidation));\r
                         schema.Write (Console.Out);\r
                         Console.WriteLine ();\r
@@ -62,16 +79,20 @@ namespace Mono.Util {
                 ///     Given a Type and its associated schema type, add aa '<xs;element>' node \r
                 ///     to the schema.\r
                 /// </summary>\r
-                static XmlSchemaElement GenerateSchemaElement (Type type, XmlSchemaType schemaType) \r
+                static XmlSchemaElement WriteSchemaElement (Type type, XmlSchemaType schemaType) \r
                 {\r
                         XmlSchemaElement schemaElement = new XmlSchemaElement ();\r
                         schemaElement.Name = type.Name;\r
-                        \r
+\r
                         if (schemaType.QualifiedName == null || schemaType.QualifiedName == XmlQualifiedName.Empty)\r
                                 schemaElement.SchemaTypeName = new XmlQualifiedName (schemaType.Name);\r
+                        \r
                         else\r
                                 schemaElement.SchemaTypeName = schemaType.QualifiedName;\r
-                        \r
+                       \r
+                        if (schemaType is XmlSchemaComplexType)\r
+                                schemaElement.IsNillable = true;\r
+\r
                         return schemaElement;\r
                 }\r
                \r
@@ -85,71 +106,255 @@ namespace Mono.Util {
                 ///     From a Type, create a corresponding ComplexType node to\r
                 ///     represent this Type.\r
                 /// </summary>\r
-                static XmlSchemaType GenerateSchemaType (Type type) \r
+                static XmlSchemaType WriteSchemaType (Type type) \r
                 {\r
-                        if (generatedSchemaTypes.Contains (type.FullName))\r
+                        if (generatedSchemaTypes.Contains (type.FullName)) // Caching\r
                                 return generatedSchemaTypes [type.FullName] as XmlSchemaType;\r
 \r
-                        if (type != typeof (object) && type.BaseType != typeof (object)) \r
-                                return GenerateComplexContent (type);\r
+                        XmlSchemaType schemaType = new XmlSchemaType ();\r
+\r
+                        if (!type.IsAbstract && typeof (System.Delegate).IsAssignableFrom (type))\r
+                                return null;\r
+\r
+                        if (type.IsEnum)\r
+                                schemaType = WriteEnum (type);\r
+\r
+                        else if (type != typeof (object) && type.BaseType != typeof (object)) {\r
+                                \r
+                                try {\r
+                                        schemaType = WriteComplexType (type);\r
+                                } catch (ArgumentException e) {\r
+                                        throw e;\r
+                                }\r
+\r
+                        } else {\r
+                                XmlSchemaComplexType complexType = new XmlSchemaComplexType ();\r
+                                complexType.Name = type.Name;\r
+                                FieldInfo [] fields = type.GetFields (flags);\r
+                                PropertyInfo [] properties = type.GetProperties (flags);\r
+                                XmlSchemaSequence sequence;\r
+\r
+                                try {\r
+                                        sequence = PopulateSequence (fields, properties);\r
+                                } catch (ArgumentException e) {\r
+                                        throw new ArgumentException (String.Format ("There is an error in '{0}'\n\t{1}", type.Name, e.Message));\r
+                                }\r
+                                complexType.Particle = sequence;                        \r
+                                generatedSchemaTypes.Add (type.FullName, complexType);\r
+\r
+                                schemaType = complexType;\r
+                        }\r
+\r
+                        return schemaType;\r
+                }\r
+\r
+                static XmlSchemaType WriteEnum (Type type) \r
+                {\r
+                        if (type.IsEnum == false)\r
+                                throw new Exception (String.Format ("{0} is not an enumeration.", type.Name));\r
+\r
+                        XmlSchemaSimpleType simpleType = new XmlSchemaSimpleType ();\r
+                        simpleType.Name = type.Name;\r
+                        FieldInfo [] fields = type.GetFields ();\r
                         \r
+                        XmlSchemaSimpleTypeRestriction simpleRestriction = new XmlSchemaSimpleTypeRestriction ();\r
+                        simpleType.Content = simpleRestriction;\r
+                        simpleRestriction.BaseTypeName = new XmlQualifiedName ("string", xs);\r
+\r
+                        foreach (FieldInfo field in fields) {\r
+                                if (field.IsSpecialName)\r
+                                        continue;\r
+\r
+                                XmlSchemaEnumerationFacet e = new XmlSchemaEnumerationFacet ();\r
+                                e.Value = field.Name;\r
+                                simpleRestriction.Facets.Add (e);\r
+                        }\r
+\r
+                        generatedSchemaTypes.Add (type.FullName, simpleType);\r
+                        return simpleType;\r
+                }\r
+\r
+                static XmlSchemaType WriteArray (Type type) \r
+                {\r
                         XmlSchemaComplexType complexType = new XmlSchemaComplexType ();\r
-                        complexType.Name = type.Name;\r
-                        FieldInfo [] fields = type.GetFields (instance_flag);\r
-                        XmlSchemaSequence sequence = PopulateSequence (fields);\r
+                        string type_name = type.Name.Substring (0, type.Name.Length - 2);\r
+                        complexType.Name = "ArrayOf" + type_name;\r
+\r
+                        XmlSchemaSequence sequence = new XmlSchemaSequence ();\r
+                        XmlSchemaElement element = new XmlSchemaElement ();\r
+\r
+                        element.MinOccurs = 0;\r
+                        element.MaxOccursString = "unbounded";\r
+                        element.IsNillable = true;\r
+                        element.Name = type_name.ToLower ();                   \r
+                        element.SchemaTypeName = GetQualifiedName (\r
+                                type.FullName.Substring (0, type.FullName.Length - 2));\r
+                        \r
+                        sequence.Items.Add (element);\r
                         complexType.Particle = sequence;\r
 \r
                         generatedSchemaTypes.Add (type.FullName, complexType);\r
-       \r
-                        return complexType;\r
+                        return complexType;                        \r
                 }\r
 \r
+\r
                 /// <summary>\r
-                ///     Handle schema derivation by extension.\r
+                ///     Handle derivation by extension. \r
+                ///     If type is null, it'll create a new complexType \r
+                ///     with an XmlAny node in its sequence child node.\r
                 /// </summary>\r
-                static XmlSchemaType GenerateComplexContent (Type type) \r
+                static XmlSchemaType WriteComplexType (Type type) \r
                 {\r
-                        XmlSchemaType baseSchemaType = GenerateSchemaType (type.BaseType);\r
+                        //\r
+                        // Recursively generate schema for all parent types\r
+                        //\r
+                        if (type != null && type.BaseType == typeof (object))\r
+                                return WriteSchemaType (type);\r
 \r
                         XmlSchemaComplexType complexType = new XmlSchemaComplexType ();\r
-                        complexType.Name = type.Name;\r
-\r
-                       FieldInfo [] fields = type.GetFields (instance_flag);\r
+                        XmlSchemaSequence sequence;\r
 \r
-                       XmlSchemaComplexContent content = new XmlSchemaComplexContent ();\r
+                        if (type == null) {\r
+                                complexType.IsMixed = true;\r
+                                sequence = new XmlSchemaSequence ();\r
+                                sequence.Items.Add (new XmlSchemaAny ());\r
+                                complexType.Particle = sequence;\r
+                                return complexType;\r
+                        }\r
                         XmlSchemaComplexContentExtension extension = new XmlSchemaComplexContentExtension ();\r
-                        XmlSchemaSequence sequence = PopulateSequence (fields);\r
+                        XmlSchemaComplexContent content = new XmlSchemaComplexContent ();\r
+                        \r
+                        complexType.ContentModel = content;\r
+                        content.Content = extension;\r
+\r
+                        XmlSchemaType baseSchemaType = WriteSchemaType (type.BaseType);\r
+\r
+                        complexType.Name = type.Name;\r
+\r
+                        FieldInfo [] fields = type.GetFields (flags);\r
+                        PropertyInfo [] properties = type.GetProperties (flags);\r
 \r
-                       extension.Particle = sequence;\r
+                        try {\r
+                                sequence = PopulateSequence (fields, properties);\r
+                        } catch (ArgumentException e) {\r
+                                throw new ArgumentException (String.Format ("There is an error in '{0}'\n\t{1}", type.Name, e.Message));\r
+                        }\r
+                        \r
                         extension.BaseTypeName = new XmlQualifiedName (baseSchemaType.Name);\r
-                        content.Content = extension;\r
-                        complexType.ContentModel = content;\r
+                        extension.Particle = sequence;\r
 \r
-                       return complexType;\r
+                        generatedSchemaTypes.Add (type.FullName, complexType);\r
+                        return complexType;\r
                 }       \r
 \r
-                static XmlSchemaSequence PopulateSequence (FieldInfo [] fields) \r
+                static XmlSchemaSequence PopulateSequence (FieldInfo [] fields, PropertyInfo [] properties\r
                 {\r
+                        if (fields == null && properties == null)\r
+                                return null;\r
+\r
                         XmlSchemaSequence sequence = new XmlSchemaSequence ();\r
+                        \r
+                        try {\r
+                                foreach (FieldInfo field in fields)\r
+                                        AddElement (sequence, field, field.FieldType);\r
 \r
-                        foreach (FieldInfo field in fields) {\r
-                                XmlSchemaElement fieldElement = new XmlSchemaElement ();\r
-                                fieldElement.Name = field.Name;\r
-                                fieldElement.SchemaTypeName = GetSchemaTypeQName (field);\r
-                                sequence.Items.Add (fieldElement);\r
+                        } catch (Exception e) {\r
+                                throw e;\r
                         }\r
 \r
+                        if (properties == null)\r
+                                return sequence;\r
+\r
+                        try {\r
+                                foreach (PropertyInfo property in properties)\r
+                                        AddElement (sequence, property, property.PropertyType);\r
+\r
+                        } catch (ArgumentException e) {\r
+                                throw e;\r
+                        }\r
+                        \r
                         return sequence;\r
                 }\r
 \r
-               ///<summary>\r
-               ///     Populates element nodes inside a '<xs:sequence>' node.\r
-               ///</summary>\r
-                static XmlQualifiedName GetSchemaTypeQName (FieldInfo field) \r
+                static void AddElement (XmlSchemaSequence sequence, MemberInfo member, Type type) \r
+                {\r
+                        //\r
+                        // Only read/write properties are supported.\r
+                        //\r
+                        if (member is PropertyInfo) {\r
+                                PropertyInfo p = (PropertyInfo) member;\r
+                                if (! (p.CanRead && p.CanWrite))\r
+                                        return;\r
+                        }\r
+\r
+                        //\r
+                        // readonly fields are not supported.\r
+                        //\r
+                        if (member is FieldInfo) {\r
+                                FieldInfo f = (FieldInfo) member;\r
+                                if (f.IsInitOnly || f.IsLiteral )\r
+                                        return;\r
+                        }\r
+\r
+                        //\r
+                        // delegates are not supported.\r
+                        //\r
+                        if (!type.IsAbstract && typeof (System.Delegate).IsAssignableFrom (type))\r
+                                return;\r
+\r
+                        if (type.IsArray) {\r
+                                XmlSchemaType arrayType = WriteArray (type);\r
+                                schema.Items.Add (arrayType);\r
+                        }\r
+\r
+                        XmlSchemaElement element = new XmlSchemaElement ();\r
+                        element.Name = member.Name;\r
+\r
+                        XmlQualifiedName qname = GetQualifiedName (type);\r
+\r
+                        if (qname == null)\r
+                                throw new ArgumentException (String.Format ("The type '{0}' cannot be represented in XML Schema.", type.FullName));\r
+\r
+                        if (qname != XmlQualifiedName.Empty)\r
+                                element.SchemaTypeName = qname;\r
+\r
+                        if (qname.Name == "xml") {\r
+                                element.SchemaType = WriteComplexType (null);\r
+                                element.SchemaTypeName = XmlQualifiedName.Empty; // 'xml' is just a temporary name\r
+                        }\r
+\r
+                        if (type.IsClass)\r
+                                element.MinOccurs = 0;\r
+                        else\r
+                                element.MinOccurs = 1;\r
+                        element.MaxOccurs = 1;\r
+\r
+                        sequence.Items.Add (element);\r
+                }\r
+\r
+                static XmlQualifiedName GetQualifiedName (Type type) \r
+                {\r
+                        if (type.Equals (typeof (System.Xml.XmlNode)))\r
+                                return XmlQualifiedName.Empty;\r
+\r
+                        else if (type.IsSubclassOf (typeof (System.Xml.XmlNode)))\r
+                                return new XmlQualifiedName ("xml");\r
+\r
+                        else if (type.IsArray) {\r
+                                string array_type = type.Name.Substring (0, type.Name.Length - 2);\r
+                                return new XmlQualifiedName ("ArrayOf" + array_type);\r
+                        } else \r
+                                return GetQualifiedName (type.FullName);\r
+                }\r
+\r
+                ///<summary>\r
+                ///    Populates element nodes inside a '<xs:sequence>' node.\r
+                ///</summary>\r
+                static XmlQualifiedName GetQualifiedName (string type) \r
                 {\r
                         string type_name;\r
 \r
-                        switch (field.FieldType.FullName) {\r
+                        switch (type) {\r
                                 case "System.Uri":\r
                                         type_name =  "anyURI";\r
                                         break;\r
@@ -194,21 +399,19 @@ namespace Mono.Util {
                                         break;\r
                                 case "System.UInt64":\r
                                         type_name = "unsignedLong";            \r
-                                        break;                                  \r
+                                        break;           \r
                                 default:\r
-                                        type_name = String.Empty;\r
+                                        type_name = null;\r
                                         break;\r
                         }       \r
                                 \r
-                        if (type_name == String.Empty)\r
-                                throw new Exception (String.Format ("Can't convert {0} to an applicable Schema Type", field.Name));\r
+                        if (type_name == null)\r
+                                return null;\r
 \r
-                       else {\r
+                        else {\r
                                 XmlQualifiedName name = new XmlQualifiedName (type_name, xs);\r
                                 return name;                                                \r
                         }                        \r
                 }\r
         }\r
 }\r
-\r
-\r