2 /// MonoXSD.cs -- A reflection-based tool for dealing with XML Schema.
4 /// Authors: Duncan Mak (duncan@ximian.com)
5 /// Lluis Sanchez Gual (lluis@ximian.com)
6 /// Atsushi Enomoto (atsushi@ximian.com)
8 /// Copyright (C) 2003, Duncan Mak,
13 using System
.Collections
;
16 using System
.Reflection
;
18 using System
.Xml
.Schema
;
19 using System
.Xml
.Serialization
;
21 using System
.CodeDom
.Compiler
;
22 using Microsoft
.CSharp
;
23 using Microsoft
.VisualBasic
;
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
);
79 new Driver().Run (args
);
83 Console
.WriteLine (ex
.Message
);
84 Console
.WriteLine (ex
);
88 string outputDir
= null;
91 ArrayList lookupTypes
= new ArrayList();
92 ArrayList assemblies
= new ArrayList();
94 ArrayList schemaNames
= new ArrayList();
95 ArrayList inferenceNames
= new ArrayList();
96 ArrayList elements
= new ArrayList();
97 string language
= null;
100 string providerOption
= null;
101 CodeDomProvider provider
;
103 public void Run (string[] args
)
105 ArrayList unknownFiles
= new ArrayList();
106 bool generateClasses
= false;
107 bool readingFiles
= true;
108 bool schemasOptions
= false;
109 bool assemblyOptions
= false;
110 bool generateDataset
= false;
111 bool inference
= false;
113 foreach (string arg
in args
)
115 if (!arg
.StartsWith ("--") && !arg
.StartsWith ("/") ||
116 (arg
.StartsWith ("/") && arg
.IndexOfAny (Path
.InvalidPathChars
) == -1)
119 if ((arg
.EndsWith (".dll") || arg
.EndsWith (".exe")) && !arg
.Substring (1).StartsWith ("generator:") && !arg
.Substring (1).StartsWith ("g:"))
121 if (!readingFiles
) throw new Exception (incorrectOrder
);
122 assemblies
.Add (arg
);
123 assemblyOptions
= true;
126 else if (arg
.EndsWith (".xsd"))
128 if (!readingFiles
) Error (incorrectOrder
);
129 schemaNames
.Add (arg
);
130 schemasOptions
= true;
133 else if (arg
.EndsWith (".xml"))
135 if (generateClasses
|| generateDataset
) Error (duplicatedParam
);
136 inferenceNames
.Add (arg
);
140 else if (!arg
.StartsWith ("/"))
142 if (!readingFiles
) Error (incorrectOrder
);
143 unknownFiles
.Add (arg
);
148 readingFiles
= false;
150 int i
= arg
.IndexOf (":");
151 if (i
== -1) i
= arg
.Length
;
152 string option
= arg
.Substring (1,i
-1);
153 string param
= (i
<arg
.Length
-1) ? arg
.Substring (i
+1) : "";
155 if (option
== "classes" || option
== "c")
157 if (generateClasses
|| generateDataset
|| inference
) Error (duplicatedParam
, option
);
158 generateClasses
= true;
159 schemasOptions
= true;
161 else if (option
== "dataset" || option
== "d")
163 if (generateClasses
|| generateDataset
|| inference
) Error (duplicatedParam
, option
);
164 generateDataset
= true;
165 schemasOptions
= true;
167 else if (option
== "element" || option
== "e")
169 elements
.Add (param
);
170 schemasOptions
= true;
172 else if (option
== "language" || option
== "l")
174 if (provider
!= null) Error (duplicatedParam
, option
);
175 if (language
!= null) Error (duplicatedParam
, option
);
177 schemasOptions
= true;
179 else if (option
== "namespace" || option
== "n")
181 if (namesp
!= null) Error (duplicatedParam
, option
);
183 schemasOptions
= true;
185 else if (option
== "outputdir" || option
== "o")
187 if (outputDir
!= null) Error (duplicatedParam
, option
);
190 else if (option
== "uri" || option
== "u")
192 if (uri
!= null) Error (duplicatedParam
, option
);
194 schemasOptions
= true;
196 else if (option
== "type" || option
== "t")
198 lookupTypes
.Add (param
);
199 assemblyOptions
= true;
201 else if (option
== "generator" || option
== "g")
203 providerOption
= param
;
205 else if (option
== "help" || option
== "h")
207 Console
.WriteLine (helpString
);
211 Error (unknownOption
, option
);
214 if (!schemasOptions
&& !assemblyOptions
&& !inference
)
215 Error (invalidParams
);
217 if (schemasOptions
&& assemblyOptions
)
218 Error (incompatibleArgs
);
220 if (assemblies
.Count
> 1)
221 Error (tooManyAssem
);
223 if (outputDir
== null) outputDir
= ".";
225 string typename
= null;
226 Type generatorType
= null;
228 if (language
!= null) {
231 provider
= new CSharpCodeProvider ();
234 provider
= new VBCodeProvider ();
237 typename
= StripQuot (language
);
239 generatorType
= Type
.GetType (typename
);
240 if (generatorType
== null)
241 Error (generatorTypeNotFound
, typename
);
246 if (providerOption
!= null) {
247 string param
= providerOption
;
248 int comma
= param
.IndexOf (',');
250 typename
= StripQuot (param
);
251 generatorType
= Type
.GetType (param
);
253 typename
= param
.Substring (0, comma
);
254 string asmName
= param
.Substring (comma
+ 1);
256 Assembly asm
= Assembly
.LoadFile (asmName
);
258 Assembly asm
= Assembly
.LoadFrom (asmName
);
261 Error (generatorAssemblyNotFound
, asmName
);
262 generatorType
= asm
.GetType (typename
);
264 if (generatorType
== null)
265 Error (generatorTypeNotFound
, typename
);
267 if (generatorType
!= null) {
268 if (!generatorType
.IsSubclassOf (typeof (CodeDomProvider
)))
269 Error (generatorTypeIsNotCodeGenerator
, typename
);
271 provider
= (CodeDomProvider
) Activator
.CreateInstance (generatorType
, null);
272 } catch (Exception ex
) {
273 Error (generatorThrewException
, generatorType
.AssemblyQualifiedName
.ToString () + " --> " + ex
.Message
);
275 Console
.WriteLine ("Loaded custom generator type " + generatorType
+ " .");
277 if (provider
== null)
278 provider
= new CSharpCodeProvider ();
282 if (!generateClasses
&& !generateDataset
)
283 Error (missingOutputForXsdInput
);
284 schemaNames
.AddRange (unknownFiles
);
287 else if (generateDataset
)
292 foreach (string xmlfile
in inferenceNames
) {
293 string genFile
= Path
.Combine (outputDir
, Path
.GetFileNameWithoutExtension (xmlfile
) + ".xsd");
294 DataSet ds
= new DataSet ();
295 ds
.InferXmlSchema (xmlfile
, null);
296 ds
.WriteXmlSchema (genFile
);
297 Console
.WriteLine ("Written file " + genFile
);
302 assemblies
.AddRange (unknownFiles
);
307 public void GenerateSchemas ()
309 Assembly assembly
= null;
312 assembly
= Assembly
.LoadFrom ((string) assemblies
[0]);
316 Error (errLoadAssembly
, ex
.Message
);
321 if (lookupTypes
.Count
> 0)
323 types
= new Type
[lookupTypes
.Count
];
324 for (int n
=0; n
<lookupTypes
.Count
; n
++)
326 Type t
= assembly
.GetType ((string)lookupTypes
[n
]);
327 if (t
== null) Error (typeNotFound
, (string)lookupTypes
[n
]);
332 types
= assembly
.GetExportedTypes ();
334 XmlReflectionImporter ri
= new XmlReflectionImporter ();
335 XmlSchemas schemas
= new XmlSchemas ();
336 XmlSchemaExporter sx
= new XmlSchemaExporter (schemas
);
338 foreach (Type type
in types
)
340 XmlTypeMapping tm
= ri
.ImportTypeMapping (type
);
341 sx
.ExportTypeMapping (tm
);
344 if (schemas
.Count
== 1)
346 string fileName
= Path
.Combine (outputDir
, "schema.xsd");
347 WriteSchema (fileName
, schemas
[0]);
351 for (int n
=0; n
<schemas
.Count
; n
++)
353 string fileName
= Path
.Combine (outputDir
, "schema" + n
+ ".xsd");
354 WriteSchema (fileName
, schemas
[n
]);
359 void WriteSchema (string fileName
, XmlSchema schema
)
361 StreamWriter sw
= new StreamWriter (fileName
);
364 Console
.WriteLine ("Written file " + fileName
);
367 public void GenerateClasses ()
369 if (namesp
== null) namesp
= "Schemas";
370 if (uri
== null) uri
= "";
371 string targetFile
= "";
373 XmlSchemas schemas
= new XmlSchemas();
374 foreach (string fileName
in schemaNames
)
376 StreamReader sr
= new StreamReader (fileName
);
377 schemas
.Add (XmlSchema
.Read (sr
, null));
380 if (targetFile
== "") targetFile
= Path
.GetFileNameWithoutExtension (fileName
);
381 else targetFile
+= "_" + Path
.GetFileNameWithoutExtension (fileName
);
384 targetFile
+= "." + provider
.FileExtension
;
386 CodeCompileUnit cunit
= new CodeCompileUnit ();
387 CodeNamespace codeNamespace
= new CodeNamespace (namesp
);
388 cunit
.Namespaces
.Add (codeNamespace
);
389 codeNamespace
.Comments
.Add (new CodeCommentStatement ("\nThis source code was auto-generated by MonoXSD\n"));
391 // Locate elements to generate
393 ArrayList qnames
= new ArrayList ();
394 if (elements
.Count
> 0)
396 foreach (string name
in elements
)
397 qnames
.Add (new XmlQualifiedName (name
, uri
));
401 foreach (XmlSchema schema
in schemas
) {
402 if (!schema
.IsCompiled
) schema
.Compile (null);
403 foreach (XmlSchemaObject ob
in schema
.Items
)
404 if (ob
is XmlSchemaElement
)
405 qnames
.Add (((XmlSchemaElement
)ob
).QualifiedName
);
409 // Import schemas and generate the class model
411 XmlSchemaImporter importer
= new XmlSchemaImporter (schemas
);
412 XmlCodeExporter sx
= new XmlCodeExporter (codeNamespace
, cunit
);
414 ArrayList maps
= new ArrayList();
416 foreach (XmlQualifiedName qname
in qnames
)
418 XmlTypeMapping tm
= importer
.ImportTypeMapping (qname
);
419 if (tm
!= null) maps
.Add (tm
);
422 foreach (XmlTypeMapping tm
in maps
)
424 sx
.ExportTypeMapping (tm
);
429 ICodeGenerator gen
= provider
.CreateGenerator();
431 string genFile
= Path
.Combine (outputDir
, targetFile
);
432 StreamWriter sw
= new StreamWriter(genFile
, false);
433 gen
.GenerateCodeFromCompileUnit (cunit
, sw
, new CodeGeneratorOptions());
436 Console
.WriteLine ("Written file " + genFile
);
439 public void GenerateDataset ()
441 if (namesp
== null) namesp
= "Schemas";
442 if (uri
== null) uri
= "";
443 string targetFile
= "";
445 DataSet dataset
= new DataSet ();
446 foreach (string fileName
in schemaNames
)
448 dataset
.ReadXmlSchema (fileName
);
450 if (targetFile
== "") targetFile
= Path
.GetFileNameWithoutExtension (fileName
);
451 else targetFile
+= "_" + Path
.GetFileNameWithoutExtension (fileName
);
454 targetFile
+= "." + provider
.FileExtension
;
456 CodeCompileUnit cunit
= new CodeCompileUnit ();
457 CodeNamespace codeNamespace
= new CodeNamespace (namesp
);
458 cunit
.Namespaces
.Add (codeNamespace
);
459 codeNamespace
.Comments
.Add (new CodeCommentStatement ("\nThis source code was auto-generated by MonoXSD\n"));
463 ICodeGenerator gen
= provider
.CreateGenerator ();
465 TypedDataSetGenerator
.Generate (dataset
, codeNamespace
, gen
);
467 string genFile
= Path
.Combine (outputDir
, targetFile
);
468 StreamWriter sw
= new StreamWriter(genFile
, false);
469 gen
.GenerateCodeFromCompileUnit (cunit
, sw
, new CodeGeneratorOptions());
472 Console
.WriteLine ("Written file " + genFile
);
475 public void Error (string msg
)
477 throw new Exception (msg
);
480 public void Error (string msg
, string param
)
482 throw new Exception (string.Format(msg
,param
));
485 private string StripQuot (string input
)
487 if (input
.Length
< 2)
489 if (input
[0] == '"' && input
[input
.Length
-1] == '"' ||
490 input
[0] == '\'' && input
[input
.Length
- 1] == '\'')
491 return input
.Substring (1, input
.Length
- 2);