Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

508 lines
16 KiB
C#

///
/// MonoXSD.cs -- A reflection-based tool for dealing with XML Schema.
///
/// Authors: Duncan Mak (duncan@ximian.com)
/// Lluis Sanchez Gual (lluis@ximian.com)
/// Atsushi Enomoto (atsushi@ximian.com)
///
/// Copyright (C) 2003, Duncan Mak,
/// Ximian, Inc.
///
using System;
using System.Collections;
using System.Data;
using System.IO;
using System.Reflection;
using System.Xml;
using System.Xml.Schema;
using System.Xml.Serialization;
using System.CodeDom;
using System.CodeDom.Compiler;
using Microsoft.CSharp;
using Microsoft.VisualBasic;
namespace Mono.Util {
public class Driver
{
public static readonly string helpString =
"xsd.exe - a utility for generating schema or class files\n\n" +
"xsd.exe <schema>.xsd /classes [/element:NAME] [/language:NAME]\n" +
" [/namespace:NAME] [/outputdir:PATH] [/uri:NAME]\n\n" +
"xsd.exe <schema>.xsd /dataset [/element:NAME] [/language:NAME]\n" +
" [/namespace:NAME] [/outputdir:PATH] [/uri:NAME]\n\n" +
"xsd.exe <assembly>.dll|<assembly>.exe [/outputdir:PATH] [/type:NAME]\n\n" +
"xsd.exe <instance>.xml [<instance>.xml ...] [/outputdir:PATH]\n\n" +
" /c /classes Generate classes for the specified schema.\n" +
" /d /dataset Generate typed dataset classes for the specified schema.\n" +
" /e /element:NAME Element from schema to generate code for.\n" +
" Multiple elements can be specified.\n" +
" /u /uri:NAME Namespace uri of the elements to generate code for.\n" +
" /l /language:NAME The language, or type name of custom CodeDomProvider\n" +
" to use for the generated code.\n" +
" Shorthand specifiers are: \"CS\" (C#) and \"VB\" (VB.NET).\n" +
" For type name, assembly qualified name is required.\n" +
" /g /generator:TYPE Code Generator type name, followed by ','\n" +
" and assembly file name.\n" +
" /o /outputdir:PATH The directory where to generate the code or schemas.\n" +
" /n /namespace:NAME Namespace for the generated code.\n" +
" /t /type:NAME Type for which to generate an xml schema.\n" +
" Multiple types can be specified.\n" +
" /h /help Output this help.\n";
static readonly string incorrectOrder = "Options must be specified after Assembly or schema file names";
static readonly string duplicatedParam = "The option {0} cannot be specified more that once";
static readonly string unknownOption = "Unknown option {0}";
static readonly string incompatibleArgs = "Cannot mix options for generating schemas and for generatic classes";
static readonly string invalidParams = "Invalid parameters";
static readonly string tooManyAssem = "Only one assembly name can be specified";
static readonly string errLoadAssembly = "Could not load assembly: {0}";
static readonly string typeNotFound = "Type {0} not found in the specified assembly";
static readonly string languageNotSupported = "The language {0} is not supported";
static readonly string missingOutputForXsdInput = "Can only generate one of classes or datasets.";
static readonly string generatorAssemblyNotFound = "Could not load code provider assembly file: {0}";
static readonly string generatorTypeNotFound = "Could not find specified code provider type: {0}";
static readonly string generatorTypeIsNotCodeGenerator = "Specified code provider type was not CodeDomProvider: {0}";
static readonly string generatorThrewException = "Specified CodeDomProvider raised an error while creating its instance: {0}";
static void Main (string [] args)
{
if (args.Length < 1) {
Console.WriteLine (helpString);
Environment.Exit (0);
}
try
{
new Driver().Run (args);
}
catch (ApplicationException ex)
{
Console.WriteLine (ex.Message);
}
catch (Exception ex)
{
Console.WriteLine (ex);
}
}
string outputDir = null;
ArrayList lookupTypes = new ArrayList();
ArrayList assemblies = new ArrayList();
ArrayList schemaNames = new ArrayList();
ArrayList inferenceNames = new ArrayList();
ArrayList elements = new ArrayList();
string language = null;
string namesp = null;
string uri = null;
string providerOption = null;
CodeDomProvider provider;
public void Run (string[] args)
{
ArrayList unknownFiles = new ArrayList();
bool generateClasses = false;
bool readingFiles = true;
bool schemasOptions = false;
bool assemblyOptions = false;
bool generateDataset = false;
bool inference = false;
foreach (string arg in args)
{
if (!arg.StartsWith ("--") && !arg.StartsWith ("/") ||
(arg.StartsWith ("/") && arg.IndexOfAny (Path.InvalidPathChars) == -1)
)
{
if ((arg.EndsWith (".dll") || arg.EndsWith (".exe")) && !arg.Substring (1).StartsWith ("generator:") && !arg.Substring (1).StartsWith ("g:"))
{
if (!readingFiles) throw new ApplicationException (incorrectOrder);
assemblies.Add (arg);
assemblyOptions = true;
continue;
}
else if (arg.EndsWith (".xsd"))
{
if (!readingFiles) Error (incorrectOrder);
schemaNames.Add (arg);
schemasOptions = true;
continue;
}
else if (arg.EndsWith (".xml"))
{
if (generateClasses || generateDataset) Error (duplicatedParam);
inferenceNames.Add (arg);
inference = true;
continue;
}
else if (!arg.StartsWith ("/"))
{
if (!readingFiles) Error (incorrectOrder);
unknownFiles.Add (arg);
continue;
}
}
readingFiles = false;
int i = arg.IndexOf (":");
if (i == -1) i = arg.Length;
string option = arg.Substring (1,i-1);
string param = (i<arg.Length-1) ? arg.Substring (i+1) : "";
if (option == "classes" || option == "c")
{
if (generateClasses || generateDataset || inference) Error (duplicatedParam, option);
generateClasses = true;
schemasOptions = true;
}
else if (option == "dataset" || option == "d")
{
if (generateClasses || generateDataset || inference) Error (duplicatedParam, option);
generateDataset = true;
schemasOptions = true;
}
else if (option == "element" || option == "e")
{
elements.Add (param);
schemasOptions = true;
}
else if (option == "language" || option == "l")
{
if (provider != null) Error (duplicatedParam, option);
if (language != null) Error (duplicatedParam, option);
language = param;
schemasOptions = true;
}
else if (option == "namespace" || option == "n")
{
if (namesp != null) Error (duplicatedParam, option);
namesp = param;
schemasOptions = true;
}
else if (option == "outputdir" || option == "o")
{
if (outputDir != null) Error (duplicatedParam, option);
outputDir = param;
}
else if (option == "uri" || option == "u")
{
if (uri != null) Error (duplicatedParam, option);
uri = param;
schemasOptions = true;
}
else if (option == "type" || option == "t")
{
lookupTypes.Add (param);
assemblyOptions = true;
}
else if (option == "generator" || option == "g")
{
providerOption = param;
}
else if (option == "help" || option == "h")
{
Console.WriteLine (helpString);
return;
}
else if (option == "nologo")
{
// ignore, since we do not output a logo anyway
}
else
Error (unknownOption, option);
}
if (!schemasOptions && !assemblyOptions && !inference)
Error (invalidParams);
if (schemasOptions && assemblyOptions)
Error (incompatibleArgs);
if (assemblies.Count > 1)
Error (tooManyAssem);
if (outputDir == null) outputDir = ".";
string typename = null;
Type generatorType = null;
if (language != null) {
switch (language) {
case "CS":
provider = new CSharpCodeProvider ();
break;
case "VB":
provider = new VBCodeProvider ();
break;
default:
typename = StripQuot (language);
generatorType = Type.GetType (typename);
if (generatorType == null)
Error (generatorTypeNotFound, typename);
break;
}
}
if (providerOption != null) {
string param = providerOption;
int comma = param.IndexOf (',');
if (comma < 0) {
typename = StripQuot (param);
generatorType = Type.GetType (param);
} else {
typename = param.Substring (0, comma);
string asmName = param.Substring (comma + 1);
Assembly asm = Assembly.LoadFile (asmName);
if (asm == null)
Error (generatorAssemblyNotFound, asmName);
generatorType = asm.GetType (typename);
}
if (generatorType == null)
Error (generatorTypeNotFound, typename);
}
if (generatorType != null) {
if (!generatorType.IsSubclassOf (typeof (CodeDomProvider)))
Error (generatorTypeIsNotCodeGenerator, typename);
try {
provider = (CodeDomProvider) Activator.CreateInstance (generatorType, null);
} catch (Exception ex) {
Error (generatorThrewException, generatorType.AssemblyQualifiedName.ToString () + " --> " + ex.Message);
}
Console.WriteLine ("Loaded custom generator type " + generatorType + " .");
}
if (provider == null)
provider = new CSharpCodeProvider ();
if (schemasOptions)
{
if (!generateClasses && !generateDataset)
Error (missingOutputForXsdInput);
schemaNames.AddRange (unknownFiles);
if (generateClasses)
GenerateClasses ();
else if (generateDataset)
GenerateDataset ();
}
else if (inference)
{
foreach (string xmlfile in inferenceNames) {
string genFile = Path.Combine (outputDir, Path.GetFileNameWithoutExtension (xmlfile) + ".xsd");
DataSet ds = new DataSet ();
ds.InferXmlSchema (xmlfile, null);
ds.WriteXmlSchema (genFile);
Console.WriteLine ("Written file " + genFile);
}
}
else
{
assemblies.AddRange (unknownFiles);
GenerateSchemas ();
}
}
public void GenerateSchemas ()
{
Assembly assembly = null;
try
{
assembly = Assembly.LoadFrom ((string) assemblies [0]);
}
catch (Exception ex)
{
Error (errLoadAssembly, ex.Message);
}
Type[] types;
if (lookupTypes.Count > 0)
{
types = new Type [lookupTypes.Count];
for (int n=0; n<lookupTypes.Count; n++)
{
Type t = assembly.GetType ((string)lookupTypes[n]);
if (t == null) Error (typeNotFound, (string)lookupTypes[n]);
types[n] = t;
}
}
else
types = assembly.GetExportedTypes ();
XmlReflectionImporter ri = new XmlReflectionImporter ();
XmlSchemas schemas = new XmlSchemas ();
XmlSchemaExporter sx = new XmlSchemaExporter (schemas);
foreach (Type type in types)
{
XmlTypeMapping tm = ri.ImportTypeMapping (type);
sx.ExportTypeMapping (tm);
}
if (schemas.Count == 1)
{
string fileName = Path.Combine (outputDir, "schema.xsd");
WriteSchema (fileName, schemas [0]);
}
else
{
for (int n=0; n<schemas.Count; n++)
{
string fileName = Path.Combine (outputDir, "schema" + n + ".xsd");
WriteSchema (fileName, schemas [n]);
}
}
}
void WriteSchema (string fileName, XmlSchema schema)
{
StreamWriter sw = new StreamWriter (fileName);
schema.Write (sw);
sw.Close ();
Console.WriteLine ("Written file " + fileName);
}
public void GenerateClasses ()
{
if (namesp == null) namesp = "Schemas";
if (uri == null) uri = "";
string targetFile = "";
XmlSchemas schemas = new XmlSchemas();
foreach (string fileName in schemaNames)
{
StreamReader sr = new StreamReader (fileName);
schemas.Add (XmlSchema.Read (sr, new ValidationEventHandler (HandleValidationError)));
sr.Close ();
if (targetFile == "") targetFile = Path.GetFileNameWithoutExtension (fileName);
else targetFile += "_" + Path.GetFileNameWithoutExtension (fileName);
}
targetFile += "." + provider.FileExtension;
CodeCompileUnit cunit = new CodeCompileUnit ();
CodeNamespace codeNamespace = new CodeNamespace (namesp);
cunit.Namespaces.Add (codeNamespace);
codeNamespace.Comments.Add (new CodeCommentStatement ("\nThis source code was auto-generated by MonoXSD\n"));
// Locate elements to generate
ArrayList qnames = new ArrayList ();
if (elements.Count > 0)
{
foreach (string name in elements)
qnames.Add (new XmlQualifiedName (name, uri));
}
else
{
foreach (XmlSchema schema in schemas) {
if (!schema.IsCompiled) schema.Compile (new ValidationEventHandler (HandleValidationError));
foreach (XmlSchemaElement el in schema.Elements.Values)
if (!qnames.Contains (el.QualifiedName))
qnames.Add (el.QualifiedName);
}
}
// Import schemas and generate the class model
XmlSchemaImporter importer = new XmlSchemaImporter (schemas);
XmlCodeExporter sx = new XmlCodeExporter (codeNamespace, cunit);
ArrayList maps = new ArrayList();
foreach (XmlQualifiedName qname in qnames)
{
XmlTypeMapping tm = importer.ImportTypeMapping (qname);
if (tm != null) maps.Add (tm);
}
foreach (XmlTypeMapping tm in maps)
{
sx.ExportTypeMapping (tm);
}
// Generate the code
ICodeGenerator gen = provider.CreateGenerator();
string genFile = Path.Combine (outputDir, targetFile);
StreamWriter sw = new StreamWriter(genFile, false);
gen.GenerateCodeFromCompileUnit (cunit, sw, new CodeGeneratorOptions());
sw.Close();
Console.WriteLine ("Written file " + genFile);
}
public void GenerateDataset ()
{
if (namesp == null) namesp = "";
if (uri == null) uri = "";
string targetFile = "";
DataSet dataset = new DataSet ();
foreach (string fileName in schemaNames)
{
dataset.ReadXmlSchema (fileName);
if (targetFile == "") targetFile = Path.GetFileNameWithoutExtension (fileName);
else targetFile += "_" + Path.GetFileNameWithoutExtension (fileName);
}
targetFile += "." + provider.FileExtension;
CodeCompileUnit cunit = new CodeCompileUnit ();
CodeNamespace codeNamespace = new CodeNamespace (namesp);
cunit.Namespaces.Add (codeNamespace);
codeNamespace.Comments.Add (new CodeCommentStatement ("\nThis source code was auto-generated by MonoXSD\n"));
// Generate the code
ICodeGenerator gen = provider.CreateGenerator ();
TypedDataSetGenerator.Generate (dataset, codeNamespace, gen);
string genFile = Path.Combine (outputDir, targetFile);
StreamWriter sw = new StreamWriter(genFile, false);
gen.GenerateCodeFromCompileUnit (cunit, sw, new CodeGeneratorOptions());
sw.Close();
Console.WriteLine ("Written file " + genFile);
}
void HandleValidationError (object o, ValidationEventArgs e)
{
Console.WriteLine ("{0}: {1} {2}",
e.Severity == XmlSeverityType.Error ? "Error" : "Warning",
e.Message,
e.Exception != null ? e.Exception.Message : null);
}
public void Error (string msg)
{
throw new ApplicationException (msg);
}
public void Error (string msg, string param)
{
throw new ApplicationException (string.Format(msg,param));
}
private string StripQuot (string input)
{
if (input.Length < 2)
return input;
if (input [0] == '"' && input [input.Length -1] == '"' ||
input [0] == '\'' && input [input.Length - 1] == '\'')
return input.Substring (1, input.Length - 2);
else
return language;
}
}
}