Merge branch 'master' into mono4-continuations_fix
[mono.git] / mcs / tools / mono-xsd / NewMonoXSD.cs
old mode 100755 (executable)
new mode 100644 (file)
index 513c2d4..48caa8d
@@ -1,8 +1,9 @@
 ///\r
 /// MonoXSD.cs -- A reflection-based tool for dealing with XML Schema.\r
 ///\r
-/// Author: Duncan Mak (duncan@ximian.com)\r
-///         Lluis Sanchez Gual (lluis@ximian.com)\r
+/// Authors: Duncan Mak (duncan@ximian.com)\r
+///          Lluis Sanchez Gual (lluis@ximian.com)\r
+///          Atsushi Enomoto (atsushi@ximian.com)\r
 ///\r
 /// Copyright (C) 2003, Duncan Mak,\r
 ///                     Ximian, Inc.\r
@@ -10,6 +11,7 @@
 \r
 using System;\r
 using System.Collections;\r
+using System.Data;\r
 using System.IO;\r
 using System.Reflection;\r
 using System.Xml;\r
@@ -18,6 +20,7 @@ using System.Xml.Serialization;
 using System.CodeDom;\r
 using System.CodeDom.Compiler;\r
 using Microsoft.CSharp;\r
+using Microsoft.VisualBasic;\r
 \r
 namespace Mono.Util {\r
 \r
@@ -27,13 +30,22 @@ namespace Mono.Util {
                        "xsd.exe - a utility for generating schema or class files\n\n" +\r
                        "xsd.exe <schema>.xsd /classes [/element:NAME] [/language:NAME]\n" +\r
                        "            [/namespace:NAME] [/outputdir:PATH] [/uri:NAME]\n\n" +\r
+                       "xsd.exe <schema>.xsd /dataset [/element:NAME] [/language:NAME]\n" +\r
+                       "            [/namespace:NAME] [/outputdir:PATH] [/uri:NAME]\n\n" +\r
+\r
                        "xsd.exe <assembly>.dll|<assembly>.exe [/outputdir:PATH] [/type:NAME]\n\n" +\r
+                       "xsd.exe <instance>.xml [<instance>.xml ...] [/outputdir:PATH]\n\n" +\r
                        "   /c /classes        Generate classes for the specified schema.\n" +\r
+                       "   /d /dataset        Generate typed dataset classes for the specified schema.\n" +\r
                        "   /e /element:NAME   Element from schema to generate code for.\n" +\r
                        "                      Multiple elements can be specified.\n" +\r
                        "   /u /uri:NAME       Namespace uri of the elements to generate code for.\n" +\r
-                       "   /l /language:NAME  The language to use for the generated code.\n" +\r
-                       "                      Currently, the only supported language is CS (C#).\n" +\r
+                       "   /l /language:NAME  The language, or type name of custom CodeDomProvider\n" +\r
+                       "                      to use for the generated code.\n" +\r
+                       "                      Shorthand specifiers are: \"CS\" (C#) and \"VB\" (VB.NET).\n" +\r
+                       "                      For type name, assembly qualified name is required.\n" +\r
+                       "   /g /generator:TYPE Code Generator type name, followed by ','\n" + \r
+                       "                      and assembly file name.\n" +\r
                        "   /o /outputdir:PATH The directory where to generate the code or schemas.\n" +\r
                        "   /n /namespace:NAME Namespace for the generated code.\n" +\r
                        "   /t /type:NAME      Type for which to generate an xml schema.\n" +\r
@@ -49,7 +61,11 @@ namespace Mono.Util {
                static readonly string errLoadAssembly = "Could not load assembly: {0}";\r
                static readonly string typeNotFound = "Type {0} not found in the specified assembly";\r
                static readonly string languageNotSupported = "The language {0} is not supported";\r
-               \r
+               static readonly string missingOutputForXsdInput = "Can only generate one of classes or datasets.";\r
+               static readonly string generatorAssemblyNotFound = "Could not load code provider assembly file: {0}";\r
+               static readonly string generatorTypeNotFound = "Could not find specified code provider type: {0}";\r
+               static readonly string generatorTypeIsNotCodeGenerator = "Specified code provider type was not CodeDomProvider: {0}";\r
+               static readonly string generatorThrewException = "Specified CodeDomProvider raised an error while creating its instance: {0}";\r
 \r
                static void Main (string [] args)\r
                {\r
@@ -62,9 +78,12 @@ namespace Mono.Util {
                        {\r
                                new Driver().Run (args);\r
                        }\r
-                       catch (Exception ex)\r
+                       catch (ApplicationException ex)\r
                        {\r
                                Console.WriteLine (ex.Message);\r
+                       }\r
+                       catch (Exception ex)\r
+                       {\r
                                Console.WriteLine (ex);\r
                        }\r
                }\r
@@ -76,10 +95,13 @@ namespace Mono.Util {
                ArrayList assemblies = new ArrayList();\r
 \r
                ArrayList schemaNames = new ArrayList();\r
+               ArrayList inferenceNames = new ArrayList();\r
                ArrayList elements = new ArrayList();\r
                string language = null;\r
                string namesp = null;\r
                string uri = null;\r
+               string providerOption = null;\r
+               CodeDomProvider provider;\r
 \r
                public void Run (string[] args)\r
                {\r
@@ -88,28 +110,42 @@ namespace Mono.Util {
                        bool readingFiles = true;\r
                        bool schemasOptions = false;\r
                        bool assemblyOptions = false;\r
+                       bool generateDataset = false;\r
+                       bool inference = false;\r
 \r
                        foreach (string arg in args)\r
                        {\r
-                               if (arg.EndsWith (".dll") || arg.EndsWith (".exe"))\r
+                               if (!arg.StartsWith ("--") && !arg.StartsWith ("/") ||\r
+                                       (arg.StartsWith ("/") && arg.IndexOfAny (Path.InvalidPathChars) == -1)\r
+                                       ) \r
                                {\r
-                                       if (!readingFiles) throw new Exception (incorrectOrder);\r
-                                       assemblies.Add (arg);\r
-                                       assemblyOptions = true;\r
-                                       continue;\r
-                               }\r
-                               else if (arg.EndsWith (".xsd"))\r
-                               {\r
-                                       if (!readingFiles) Error (incorrectOrder);\r
-                                       schemaNames.Add (arg);\r
-                                       schemasOptions = true;\r
-                                       continue;\r
-                               }\r
-                               else if (!arg.StartsWith ("/") && !arg.StartsWith ("-"))\r
-                               {\r
-                                       if (!readingFiles) Error (incorrectOrder);\r
-                                       unknownFiles.Add (arg);\r
-                                       continue;\r
+                                       if ((arg.EndsWith (".dll") || arg.EndsWith (".exe")) && !arg.Substring (1).StartsWith ("generator:") && !arg.Substring (1).StartsWith ("g:"))\r
+                                       {\r
+                                               if (!readingFiles) throw new ApplicationException (incorrectOrder);\r
+                                               assemblies.Add (arg);\r
+                                               assemblyOptions = true;\r
+                                               continue;\r
+                                       }\r
+                                       else if (arg.EndsWith (".xsd"))\r
+                                       {\r
+                                               if (!readingFiles) Error (incorrectOrder);\r
+                                               schemaNames.Add (arg);\r
+                                               schemasOptions = true;\r
+                                               continue;\r
+                                       }\r
+                                       else if (arg.EndsWith (".xml"))\r
+                                       {\r
+                                               if (generateClasses || generateDataset) Error (duplicatedParam);\r
+                                               inferenceNames.Add (arg);\r
+                                               inference = true;\r
+                                               continue;\r
+                                       }\r
+                                       else if (!arg.StartsWith ("/"))\r
+                                       {\r
+                                               if (!readingFiles) Error (incorrectOrder);\r
+                                               unknownFiles.Add (arg);\r
+                                               continue;\r
+                                       }\r
                                }\r
 \r
                                readingFiles = false;\r
@@ -121,10 +157,16 @@ namespace Mono.Util {
 \r
                                if (option == "classes" || option == "c")\r
                                {\r
-                                       if (generateClasses) Error (duplicatedParam, option);\r
+                                       if (generateClasses || generateDataset || inference) Error (duplicatedParam, option);\r
                                        generateClasses = true;\r
                                        schemasOptions = true;\r
                                }\r
+                               else if (option == "dataset" || option == "d")\r
+                               {\r
+                                       if (generateClasses || generateDataset || inference) Error (duplicatedParam, option);\r
+                                       generateDataset = true;\r
+                                       schemasOptions = true;\r
+                               }\r
                                else if (option == "element" || option == "e")\r
                                {\r
                                        elements.Add (param);\r
@@ -132,6 +174,7 @@ namespace Mono.Util {
                                }\r
                                else if (option == "language" || option == "l")\r
                                {\r
+                                       if (provider != null) Error (duplicatedParam, option);\r
                                        if (language != null) Error (duplicatedParam, option);\r
                                        language = param;\r
                                        schemasOptions = true;\r
@@ -158,16 +201,24 @@ namespace Mono.Util {
                                        lookupTypes.Add (param);\r
                                        assemblyOptions = true;\r
                                }\r
+                               else if (option == "generator" || option == "g")\r
+                               {\r
+                                       providerOption = param;\r
+                               }\r
                                else if (option == "help" || option == "h")\r
                                {\r
                                        Console.WriteLine (helpString);\r
                                        return;\r
                                }\r
+                               else if (option == "nologo")\r
+                               {\r
+                                       // ignore, since we do not output a logo anyway\r
+                               }\r
                                else\r
                                        Error (unknownOption, option);\r
                        }\r
 \r
-                       if (!schemasOptions && !assemblyOptions)\r
+                       if (!schemasOptions && !assemblyOptions && !inference)\r
                                Error (invalidParams);\r
 \r
                        if (schemasOptions && assemblyOptions)\r
@@ -177,11 +228,77 @@ namespace Mono.Util {
                                Error (tooManyAssem);\r
 \r
                        if (outputDir == null) outputDir = ".";\r
-                               \r
+\r
+                       string typename = null;\r
+                       Type generatorType = null;\r
+\r
+                       if (language != null) {\r
+                               switch (language) {\r
+                               case "CS":\r
+                                       provider = new CSharpCodeProvider ();\r
+                                       break;\r
+                               case "VB":\r
+                                       provider = new VBCodeProvider ();\r
+                                       break;\r
+                               default:\r
+                                       typename = StripQuot (language);\r
+\r
+                                       generatorType = Type.GetType (typename);\r
+                                       if (generatorType == null)\r
+                                               Error (generatorTypeNotFound, typename);\r
+                                       break;\r
+                               }\r
+                       }\r
+\r
+                       if (providerOption != null) {\r
+                               string param = providerOption;\r
+                               int comma = param.IndexOf (',');\r
+                               if (comma < 0) {\r
+                                       typename = StripQuot (param);\r
+                                       generatorType = Type.GetType (param);\r
+                               } else {\r
+                                       typename = param.Substring (0, comma);\r
+                                       string asmName = param.Substring (comma + 1);\r
+                                       Assembly asm = Assembly.LoadFile (asmName);\r
+                                       if (asm == null)\r
+                                               Error (generatorAssemblyNotFound, asmName);\r
+                                       generatorType = asm.GetType (typename);\r
+                               }\r
+                               if (generatorType == null)\r
+                                       Error (generatorTypeNotFound, typename);\r
+                       }\r
+                       if (generatorType != null) {\r
+                               if (!generatorType.IsSubclassOf (typeof (CodeDomProvider)))\r
+                                       Error (generatorTypeIsNotCodeGenerator, typename);\r
+                               try {\r
+                                       provider = (CodeDomProvider) Activator.CreateInstance (generatorType, null);\r
+                               } catch (Exception ex) {\r
+                                       Error (generatorThrewException, generatorType.AssemblyQualifiedName.ToString () + " --> " + ex.Message);\r
+                               }\r
+                               Console.WriteLine ("Loaded custom generator type " + generatorType + " .");\r
+                       }\r
+                       if (provider == null)\r
+                               provider = new CSharpCodeProvider ();\r
+\r
                        if (schemasOptions)\r
                        {\r
+                               if (!generateClasses && !generateDataset)\r
+                                       Error (missingOutputForXsdInput);\r
                                schemaNames.AddRange (unknownFiles);\r
-                               GenerateClasses ();\r
+                               if (generateClasses)\r
+                                       GenerateClasses ();\r
+                               else if (generateDataset)\r
+                                       GenerateDataset ();\r
+                       }\r
+                       else if (inference)\r
+                       {\r
+                               foreach (string xmlfile in inferenceNames) {\r
+                                       string genFile = Path.Combine (outputDir, Path.GetFileNameWithoutExtension (xmlfile) + ".xsd");\r
+                                       DataSet ds = new DataSet ();\r
+                                       ds.InferXmlSchema (xmlfile, null);\r
+                                       ds.WriteXmlSchema (genFile);\r
+                                       Console.WriteLine ("Written file " + genFile);\r
+                               }\r
                        }\r
                        else\r
                        {\r
@@ -252,7 +369,6 @@ namespace Mono.Util {
                \r
                public void GenerateClasses ()\r
                {\r
-                       if (language != null && language != "CS") Error (languageNotSupported, language);\r
                        if (namesp == null) namesp = "Schemas";\r
                        if (uri == null) uri = "";\r
                        string targetFile = "";\r
@@ -261,14 +377,14 @@ namespace Mono.Util {
                        foreach (string fileName in schemaNames)\r
                        {\r
                                StreamReader sr = new StreamReader (fileName);\r
-                               schemas.Add (XmlSchema.Read (sr, null));\r
+                               schemas.Add (XmlSchema.Read (sr, new ValidationEventHandler (HandleValidationError)));\r
                                sr.Close ();\r
 \r
                                if (targetFile == "") targetFile = Path.GetFileNameWithoutExtension (fileName);\r
                                else targetFile += "_" + Path.GetFileNameWithoutExtension (fileName);\r
                        }\r
 \r
-                       targetFile += ".cs";\r
+                       targetFile += "." + provider.FileExtension;\r
 \r
                        CodeCompileUnit cunit = new CodeCompileUnit ();\r
                        CodeNamespace codeNamespace = new CodeNamespace (namesp);\r
@@ -286,9 +402,10 @@ namespace Mono.Util {
                        else\r
                        {\r
                                foreach (XmlSchema schema in schemas) {\r
-                                       if (!schema.IsCompiled) schema.Compile (null);\r
-                                       foreach (XmlSchemaElement elem in schema.Elements.Values)\r
-                                               qnames.Add (elem.QualifiedName);\r
+                                       if (!schema.IsCompiled) schema.Compile (new ValidationEventHandler (HandleValidationError));\r
+                                       foreach (XmlSchemaElement el in schema.Elements.Values)\r
+                                               if (!qnames.Contains (el.QualifiedName))\r
+                                                       qnames.Add (el.QualifiedName);\r
                                }\r
                        }\r
 \r
@@ -312,7 +429,6 @@ namespace Mono.Util {
 \r
                        // Generate the code\r
                        \r
-                       CSharpCodeProvider provider = new CSharpCodeProvider();\r
                        ICodeGenerator gen = provider.CreateGenerator();\r
 \r
                        string genFile = Path.Combine (outputDir, targetFile);\r
@@ -323,14 +439,69 @@ namespace Mono.Util {
                        Console.WriteLine ("Written file " + genFile);\r
                }\r
 \r
+               public void GenerateDataset ()\r
+               {\r
+                       if (namesp == null) namesp = "";\r
+                       if (uri == null) uri = "";\r
+                       string targetFile = "";\r
+\r
+                       DataSet dataset = new DataSet ();\r
+                       foreach (string fileName in schemaNames)\r
+                       {\r
+                               dataset.ReadXmlSchema (fileName);\r
+\r
+                               if (targetFile == "") targetFile = Path.GetFileNameWithoutExtension (fileName);\r
+                               else targetFile += "_" + Path.GetFileNameWithoutExtension (fileName);\r
+                       }\r
+\r
+                       targetFile += "." + provider.FileExtension;\r
+\r
+                       CodeCompileUnit cunit = new CodeCompileUnit ();\r
+                       CodeNamespace codeNamespace = new CodeNamespace (namesp);\r
+                       cunit.Namespaces.Add (codeNamespace);\r
+                       codeNamespace.Comments.Add (new CodeCommentStatement ("\nThis source code was auto-generated by MonoXSD\n"));\r
+\r
+                       // Generate the code\r
+                       \r
+                       ICodeGenerator gen = provider.CreateGenerator ();\r
+\r
+                       TypedDataSetGenerator.Generate (dataset, codeNamespace, gen);\r
+\r
+                       string genFile = Path.Combine (outputDir, targetFile);\r
+                       StreamWriter sw = new StreamWriter(genFile, false);\r
+                       gen.GenerateCodeFromCompileUnit (cunit, sw, new CodeGeneratorOptions());\r
+                       sw.Close();\r
+\r
+                       Console.WriteLine ("Written file " + genFile);\r
+               }\r
+\r
+               void HandleValidationError (object o, ValidationEventArgs e)\r
+               {\r
+                       Console.WriteLine ("{0}: {1} {2}",\r
+                               e.Severity == XmlSeverityType.Error ? "Error" : "Warning",\r
+                               e.Message,\r
+                               e.Exception != null ? e.Exception.Message : null);\r
+               }\r
+\r
                public void Error (string msg)\r
                {\r
-                       throw new Exception (msg);\r
+                       throw new ApplicationException (msg);\r
                }\r
 \r
                public void Error (string msg, string param)\r
                {\r
-                       throw new Exception (string.Format(msg,param));\r
+                       throw new ApplicationException (string.Format(msg,param));\r
+               }\r
+\r
+               private string StripQuot (string input)\r
+               {\r
+                       if (input.Length < 2)\r
+                               return input;\r
+                       if (input [0] == '"' && input [input.Length -1] == '"' ||\r
+                               input [0] == '\'' && input [input.Length - 1] == '\'')\r
+                               return input.Substring (1, input.Length - 2);\r
+                       else\r
+                               return language;\r
                }\r
        }\r
 }\r