///\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
\r
using System;\r
using System.Collections;\r
+using System.Data;\r
using System.IO;\r
using System.Reflection;\r
using System.Xml;\r
using System.CodeDom;\r
using System.CodeDom.Compiler;\r
using Microsoft.CSharp;\r
+using Microsoft.VisualBasic;\r
\r
namespace Mono.Util {\r
\r
"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
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
{\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
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
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
\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
}\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
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
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
\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
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
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
\r
// Generate the code\r
\r
- CSharpCodeProvider provider = new CSharpCodeProvider();\r
ICodeGenerator gen = provider.CreateGenerator();\r
\r
string genFile = Path.Combine (outputDir, targetFile);\r
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