2010-06-03 Jb Evain <jbevain@novell.com>
[mcs.git] / tools / mono-xsd / NewMonoXSD.cs
blob06879b37c3d37bc0a65127325c5f1990d9f5bff4
1 ///
2 /// MonoXSD.cs -- A reflection-based tool for dealing with XML Schema.
3 ///
4 /// Authors: Duncan Mak (duncan@ximian.com)
5 /// Lluis Sanchez Gual (lluis@ximian.com)
6 /// Atsushi Enomoto (atsushi@ximian.com)
7 ///
8 /// Copyright (C) 2003, Duncan Mak,
9 /// Ximian, Inc.
10 ///
12 using System;
13 using System.Collections;
14 using System.Data;
15 using System.IO;
16 using System.Reflection;
17 using System.Xml;
18 using System.Xml.Schema;
19 using System.Xml.Serialization;
20 using System.CodeDom;
21 using System.CodeDom.Compiler;
22 using Microsoft.CSharp;
23 using Microsoft.VisualBasic;
25 namespace Mono.Util {
27 public class Driver
29 public static readonly string helpString =
30 "xsd.exe - a utility for generating schema or class files\n\n" +
31 "xsd.exe <schema>.xsd /classes [/element:NAME] [/language:NAME]\n" +
32 " [/namespace:NAME] [/outputdir:PATH] [/uri:NAME]\n\n" +
33 "xsd.exe <schema>.xsd /dataset [/element:NAME] [/language:NAME]\n" +
34 " [/namespace:NAME] [/outputdir:PATH] [/uri:NAME]\n\n" +
36 "xsd.exe <assembly>.dll|<assembly>.exe [/outputdir:PATH] [/type:NAME]\n\n" +
37 "xsd.exe <instance>.xml [<instance>.xml ...] [/outputdir:PATH]\n\n" +
38 " /c /classes Generate classes for the specified schema.\n" +
39 " /d /dataset Generate typed dataset classes for the specified schema.\n" +
40 " /e /element:NAME Element from schema to generate code for.\n" +
41 " Multiple elements can be specified.\n" +
42 " /u /uri:NAME Namespace uri of the elements to generate code for.\n" +
43 " /l /language:NAME The language, or type name of custom CodeDomProvider\n" +
44 " to use for the generated code.\n" +
45 " Shorthand specifiers are: \"CS\" (C#) and \"VB\" (VB.NET).\n" +
46 " For type name, assembly qualified name is required.\n" +
47 " /g /generator:TYPE Code Generator type name, followed by ','\n" +
48 " and assembly file name.\n" +
49 " /o /outputdir:PATH The directory where to generate the code or schemas.\n" +
50 " /n /namespace:NAME Namespace for the generated code.\n" +
51 " /t /type:NAME Type for which to generate an xml schema.\n" +
52 " Multiple types can be specified.\n" +
53 " /h /help Output this help.\n";
55 static readonly string incorrectOrder = "Options must be specified after Assembly or schema file names";
56 static readonly string duplicatedParam = "The option {0} cannot be specified more that once";
57 static readonly string unknownOption = "Unknown option {0}";
58 static readonly string incompatibleArgs = "Cannot mix options for generating schemas and for generatic classes";
59 static readonly string invalidParams = "Invalid parameters";
60 static readonly string tooManyAssem = "Only one assembly name can be specified";
61 static readonly string errLoadAssembly = "Could not load assembly: {0}";
62 static readonly string typeNotFound = "Type {0} not found in the specified assembly";
63 static readonly string languageNotSupported = "The language {0} is not supported";
64 static readonly string missingOutputForXsdInput = "Can only generate one of classes or datasets.";
65 static readonly string generatorAssemblyNotFound = "Could not load code provider assembly file: {0}";
66 static readonly string generatorTypeNotFound = "Could not find specified code provider type: {0}";
67 static readonly string generatorTypeIsNotCodeGenerator = "Specified code provider type was not CodeDomProvider: {0}";
68 static readonly string generatorThrewException = "Specified CodeDomProvider raised an error while creating its instance: {0}";
70 static void Main (string [] args)
72 if (args.Length < 1) {
73 Console.WriteLine (helpString);
74 Environment.Exit (0);
77 try
79 new Driver().Run (args);
81 catch (ApplicationException ex)
83 Console.WriteLine (ex.Message);
85 catch (Exception ex)
87 Console.WriteLine (ex);
91 string outputDir = null;
94 ArrayList lookupTypes = new ArrayList();
95 ArrayList assemblies = new ArrayList();
97 ArrayList schemaNames = new ArrayList();
98 ArrayList inferenceNames = new ArrayList();
99 ArrayList elements = new ArrayList();
100 string language = null;
101 string namesp = null;
102 string uri = null;
103 string providerOption = null;
104 CodeDomProvider provider;
106 public void Run (string[] args)
108 ArrayList unknownFiles = new ArrayList();
109 bool generateClasses = false;
110 bool readingFiles = true;
111 bool schemasOptions = false;
112 bool assemblyOptions = false;
113 bool generateDataset = false;
114 bool inference = false;
116 foreach (string arg in args)
118 if (!arg.StartsWith ("--") && !arg.StartsWith ("/") ||
119 (arg.StartsWith ("/") && arg.IndexOfAny (Path.InvalidPathChars) == -1)
122 if ((arg.EndsWith (".dll") || arg.EndsWith (".exe")) && !arg.Substring (1).StartsWith ("generator:") && !arg.Substring (1).StartsWith ("g:"))
124 if (!readingFiles) throw new ApplicationException (incorrectOrder);
125 assemblies.Add (arg);
126 assemblyOptions = true;
127 continue;
129 else if (arg.EndsWith (".xsd"))
131 if (!readingFiles) Error (incorrectOrder);
132 schemaNames.Add (arg);
133 schemasOptions = true;
134 continue;
136 else if (arg.EndsWith (".xml"))
138 if (generateClasses || generateDataset) Error (duplicatedParam);
139 inferenceNames.Add (arg);
140 inference = true;
141 continue;
143 else if (!arg.StartsWith ("/"))
145 if (!readingFiles) Error (incorrectOrder);
146 unknownFiles.Add (arg);
147 continue;
151 readingFiles = false;
153 int i = arg.IndexOf (":");
154 if (i == -1) i = arg.Length;
155 string option = arg.Substring (1,i-1);
156 string param = (i<arg.Length-1) ? arg.Substring (i+1) : "";
158 if (option == "classes" || option == "c")
160 if (generateClasses || generateDataset || inference) Error (duplicatedParam, option);
161 generateClasses = true;
162 schemasOptions = true;
164 else if (option == "dataset" || option == "d")
166 if (generateClasses || generateDataset || inference) Error (duplicatedParam, option);
167 generateDataset = true;
168 schemasOptions = true;
170 else if (option == "element" || option == "e")
172 elements.Add (param);
173 schemasOptions = true;
175 else if (option == "language" || option == "l")
177 if (provider != null) Error (duplicatedParam, option);
178 if (language != null) Error (duplicatedParam, option);
179 language = param;
180 schemasOptions = true;
182 else if (option == "namespace" || option == "n")
184 if (namesp != null) Error (duplicatedParam, option);
185 namesp = param;
186 schemasOptions = true;
188 else if (option == "outputdir" || option == "o")
190 if (outputDir != null) Error (duplicatedParam, option);
191 outputDir = param;
193 else if (option == "uri" || option == "u")
195 if (uri != null) Error (duplicatedParam, option);
196 uri = param;
197 schemasOptions = true;
199 else if (option == "type" || option == "t")
201 lookupTypes.Add (param);
202 assemblyOptions = true;
204 else if (option == "generator" || option == "g")
206 providerOption = param;
208 else if (option == "help" || option == "h")
210 Console.WriteLine (helpString);
211 return;
213 else if (option == "nologo")
215 // ignore, since we do not output a logo anyway
217 else
218 Error (unknownOption, option);
221 if (!schemasOptions && !assemblyOptions && !inference)
222 Error (invalidParams);
224 if (schemasOptions && assemblyOptions)
225 Error (incompatibleArgs);
227 if (assemblies.Count > 1)
228 Error (tooManyAssem);
230 if (outputDir == null) outputDir = ".";
232 string typename = null;
233 Type generatorType = null;
235 if (language != null) {
236 switch (language) {
237 case "CS":
238 provider = new CSharpCodeProvider ();
239 break;
240 case "VB":
241 provider = new VBCodeProvider ();
242 break;
243 default:
244 typename = StripQuot (language);
246 generatorType = Type.GetType (typename);
247 if (generatorType == null)
248 Error (generatorTypeNotFound, typename);
249 break;
253 if (providerOption != null) {
254 string param = providerOption;
255 int comma = param.IndexOf (',');
256 if (comma < 0) {
257 typename = StripQuot (param);
258 generatorType = Type.GetType (param);
259 } else {
260 typename = param.Substring (0, comma);
261 string asmName = param.Substring (comma + 1);
262 #if NET_1_1
263 Assembly asm = Assembly.LoadFile (asmName);
264 #else
265 Assembly asm = Assembly.LoadFrom (asmName);
266 #endif
267 if (asm == null)
268 Error (generatorAssemblyNotFound, asmName);
269 generatorType = asm.GetType (typename);
271 if (generatorType == null)
272 Error (generatorTypeNotFound, typename);
274 if (generatorType != null) {
275 if (!generatorType.IsSubclassOf (typeof (CodeDomProvider)))
276 Error (generatorTypeIsNotCodeGenerator, typename);
277 try {
278 provider = (CodeDomProvider) Activator.CreateInstance (generatorType, null);
279 } catch (Exception ex) {
280 Error (generatorThrewException, generatorType.AssemblyQualifiedName.ToString () + " --> " + ex.Message);
282 Console.WriteLine ("Loaded custom generator type " + generatorType + " .");
284 if (provider == null)
285 provider = new CSharpCodeProvider ();
287 if (schemasOptions)
289 if (!generateClasses && !generateDataset)
290 Error (missingOutputForXsdInput);
291 schemaNames.AddRange (unknownFiles);
292 if (generateClasses)
293 GenerateClasses ();
294 else if (generateDataset)
295 GenerateDataset ();
297 else if (inference)
299 foreach (string xmlfile in inferenceNames) {
300 string genFile = Path.Combine (outputDir, Path.GetFileNameWithoutExtension (xmlfile) + ".xsd");
301 DataSet ds = new DataSet ();
302 ds.InferXmlSchema (xmlfile, null);
303 ds.WriteXmlSchema (genFile);
304 Console.WriteLine ("Written file " + genFile);
307 else
309 assemblies.AddRange (unknownFiles);
310 GenerateSchemas ();
314 public void GenerateSchemas ()
316 Assembly assembly = null;
319 assembly = Assembly.LoadFrom ((string) assemblies [0]);
321 catch (Exception ex)
323 Error (errLoadAssembly, ex.Message);
326 Type[] types;
328 if (lookupTypes.Count > 0)
330 types = new Type [lookupTypes.Count];
331 for (int n=0; n<lookupTypes.Count; n++)
333 Type t = assembly.GetType ((string)lookupTypes[n]);
334 if (t == null) Error (typeNotFound, (string)lookupTypes[n]);
335 types[n] = t;
338 else
339 types = assembly.GetExportedTypes ();
341 XmlReflectionImporter ri = new XmlReflectionImporter ();
342 XmlSchemas schemas = new XmlSchemas ();
343 XmlSchemaExporter sx = new XmlSchemaExporter (schemas);
345 foreach (Type type in types)
347 XmlTypeMapping tm = ri.ImportTypeMapping (type);
348 sx.ExportTypeMapping (tm);
351 if (schemas.Count == 1)
353 string fileName = Path.Combine (outputDir, "schema.xsd");
354 WriteSchema (fileName, schemas [0]);
356 else
358 for (int n=0; n<schemas.Count; n++)
360 string fileName = Path.Combine (outputDir, "schema" + n + ".xsd");
361 WriteSchema (fileName, schemas [n]);
366 void WriteSchema (string fileName, XmlSchema schema)
368 StreamWriter sw = new StreamWriter (fileName);
369 schema.Write (sw);
370 sw.Close ();
371 Console.WriteLine ("Written file " + fileName);
374 public void GenerateClasses ()
376 if (namesp == null) namesp = "Schemas";
377 if (uri == null) uri = "";
378 string targetFile = "";
380 XmlSchemas schemas = new XmlSchemas();
381 foreach (string fileName in schemaNames)
383 StreamReader sr = new StreamReader (fileName);
384 schemas.Add (XmlSchema.Read (sr, new ValidationEventHandler (HandleValidationError)));
385 sr.Close ();
387 if (targetFile == "") targetFile = Path.GetFileNameWithoutExtension (fileName);
388 else targetFile += "_" + Path.GetFileNameWithoutExtension (fileName);
391 targetFile += "." + provider.FileExtension;
393 CodeCompileUnit cunit = new CodeCompileUnit ();
394 CodeNamespace codeNamespace = new CodeNamespace (namesp);
395 cunit.Namespaces.Add (codeNamespace);
396 codeNamespace.Comments.Add (new CodeCommentStatement ("\nThis source code was auto-generated by MonoXSD\n"));
398 // Locate elements to generate
400 ArrayList qnames = new ArrayList ();
401 if (elements.Count > 0)
403 foreach (string name in elements)
404 qnames.Add (new XmlQualifiedName (name, uri));
406 else
408 foreach (XmlSchema schema in schemas) {
409 if (!schema.IsCompiled) schema.Compile (new ValidationEventHandler (HandleValidationError));
410 foreach (XmlSchemaElement el in schema.Elements.Values)
411 if (!qnames.Contains (el.QualifiedName))
412 qnames.Add (el.QualifiedName);
416 // Import schemas and generate the class model
418 XmlSchemaImporter importer = new XmlSchemaImporter (schemas);
419 XmlCodeExporter sx = new XmlCodeExporter (codeNamespace, cunit);
421 ArrayList maps = new ArrayList();
423 foreach (XmlQualifiedName qname in qnames)
425 XmlTypeMapping tm = importer.ImportTypeMapping (qname);
426 if (tm != null) maps.Add (tm);
429 foreach (XmlTypeMapping tm in maps)
431 sx.ExportTypeMapping (tm);
434 // Generate the code
436 ICodeGenerator gen = provider.CreateGenerator();
438 string genFile = Path.Combine (outputDir, targetFile);
439 StreamWriter sw = new StreamWriter(genFile, false);
440 gen.GenerateCodeFromCompileUnit (cunit, sw, new CodeGeneratorOptions());
441 sw.Close();
443 Console.WriteLine ("Written file " + genFile);
446 public void GenerateDataset ()
448 if (namesp == null) namesp = "";
449 if (uri == null) uri = "";
450 string targetFile = "";
452 DataSet dataset = new DataSet ();
453 foreach (string fileName in schemaNames)
455 dataset.ReadXmlSchema (fileName);
457 if (targetFile == "") targetFile = Path.GetFileNameWithoutExtension (fileName);
458 else targetFile += "_" + Path.GetFileNameWithoutExtension (fileName);
461 targetFile += "." + provider.FileExtension;
463 CodeCompileUnit cunit = new CodeCompileUnit ();
464 CodeNamespace codeNamespace = new CodeNamespace (namesp);
465 cunit.Namespaces.Add (codeNamespace);
466 codeNamespace.Comments.Add (new CodeCommentStatement ("\nThis source code was auto-generated by MonoXSD\n"));
468 // Generate the code
470 ICodeGenerator gen = provider.CreateGenerator ();
472 TypedDataSetGenerator.Generate (dataset, codeNamespace, gen);
474 string genFile = Path.Combine (outputDir, targetFile);
475 StreamWriter sw = new StreamWriter(genFile, false);
476 gen.GenerateCodeFromCompileUnit (cunit, sw, new CodeGeneratorOptions());
477 sw.Close();
479 Console.WriteLine ("Written file " + genFile);
482 void HandleValidationError (object o, ValidationEventArgs e)
484 Console.WriteLine ("{0}: {1} {2}",
485 e.Severity == XmlSeverityType.Error ? "Error" : "Warning",
486 e.Message,
487 e.Exception != null ? e.Exception.Message : null);
490 public void Error (string msg)
492 throw new ApplicationException (msg);
495 public void Error (string msg, string param)
497 throw new ApplicationException (string.Format(msg,param));
500 private string StripQuot (string input)
502 if (input.Length < 2)
503 return input;
504 if (input [0] == '"' && input [input.Length -1] == '"' ||
505 input [0] == '\'' && input [input.Length - 1] == '\'')
506 return input.Substring (1, input.Length - 2);
507 else
508 return language;