This commit was manufactured by cvs2svn to create branch 'mono-1-0'.
[mono.git] / mcs / tools / mono-xsd / NewMonoXSD.cs
1 ///\r
2 /// MonoXSD.cs -- A reflection-based tool for dealing with XML Schema.\r
3 ///\r
4 /// Authors: Duncan Mak (duncan@ximian.com)\r
5 ///          Lluis Sanchez Gual (lluis@ximian.com)\r
6 ///          Atsushi Enomoto (atsushi@ximian.com)\r
7 ///\r
8 /// Copyright (C) 2003, Duncan Mak,\r
9 ///                     Ximian, Inc.\r
10 ///\r
11 \r
12 using System;\r
13 using System.Collections;\r
14 using System.Data;\r
15 using System.IO;\r
16 using System.Reflection;\r
17 using System.Xml;\r
18 using System.Xml.Schema;\r
19 using System.Xml.Serialization;\r
20 using System.CodeDom;\r
21 using System.CodeDom.Compiler;\r
22 using Microsoft.CSharp;\r
23 using Microsoft.VisualBasic;\r
24 \r
25 namespace Mono.Util {\r
26 \r
27         public class Driver\r
28         {\r
29                 public static readonly string helpString =\r
30                         "xsd.exe - a utility for generating schema or class files\n\n" +\r
31                         "xsd.exe <schema>.xsd /classes [/element:NAME] [/language:NAME]\n" +\r
32                         "            [/namespace:NAME] [/outputdir:PATH] [/uri:NAME]\n\n" +\r
33                         "xsd.exe <schema>.xsd /dataset [/element:NAME] [/language:NAME]\n" +\r
34                         "            [/namespace:NAME] [/outputdir:PATH] [/uri:NAME]\n\n" +\r
35 \r
36                         "xsd.exe <assembly>.dll|<assembly>.exe [/outputdir:PATH] [/type:NAME]\n\n" +\r
37                         "xsd.exe <instance>.xml [<instance>.xml ...] [/outputdir:PATH]\n\n" +\r
38                         "   /c /classes        Generate classes for the specified schema.\n" +\r
39                         "   /d /dataset        Generate typed dataset classes for the specified schema.\n" +\r
40                         "   /e /element:NAME   Element from schema to generate code for.\n" +\r
41                         "                      Multiple elements can be specified.\n" +\r
42                         "   /u /uri:NAME       Namespace uri of the elements to generate code for.\n" +\r
43                         "   /l /language:NAME  The language to use for the generated code.\n" +\r
44                         "                      Currently, the only supported language is CS (C#).\n" +\r
45                         "   /g /generator:TYPE Code Generator type name, followed by ','\n" + \r
46                         "                      and assembly file name.\n" +\r
47                         "   /o /outputdir:PATH The directory where to generate the code or schemas.\n" +\r
48                         "   /n /namespace:NAME Namespace for the generated code.\n" +\r
49                         "   /t /type:NAME      Type for which to generate an xml schema.\n" +\r
50                         "                      Multiple types can be specified.\n" +\r
51                         "   /h /help           Output this help.\n";\r
52 \r
53                 static readonly string incorrectOrder = "Options must be specified after Assembly or schema file names";\r
54                 static readonly string duplicatedParam = "The option {0} cannot be specified more that once";\r
55                 static readonly string unknownOption = "Unknown option {0}";\r
56                 static readonly string incompatibleArgs = "Cannot mix options for generating schemas and for generatic classes";\r
57                 static readonly string invalidParams = "Invalid parameters";\r
58                 static readonly string tooManyAssem = "Only one assembly name can be specified";\r
59                 static readonly string errLoadAssembly = "Could not load assembly: {0}";\r
60                 static readonly string typeNotFound = "Type {0} not found in the specified assembly";\r
61                 static readonly string languageNotSupported = "The language {0} is not supported";\r
62                 static readonly string missingOutputForXsdInput = "Can only generate one of classes or datasets.";\r
63                 static readonly string generatorAssemblyNotFound = "Could not load code provider assembly file: {0}";\r
64                 static readonly string generatorTypeNotFound = "Could not find specified code provider type: {0}";\r
65                 static readonly string generatorTypeIsNotCodeGenerator = "Specified code provider type was not CodeDomProvider: {0}";\r
66                 static readonly string generatorThrewException = "Specified CodeDomProvider raised an error while creating its instance: {0}";\r
67 \r
68                 static void Main (string [] args)\r
69                 {\r
70                         if (args.Length < 1) {\r
71                                 Console.WriteLine (helpString);\r
72                                 Environment.Exit (0);\r
73                         }\r
74 \r
75                         try\r
76                         {\r
77                                 new Driver().Run (args);\r
78                         }\r
79                         catch (Exception ex)\r
80                         {\r
81                                 Console.WriteLine (ex.Message);\r
82                                 Console.WriteLine (ex);\r
83                         }\r
84                 }\r
85 \r
86                 string outputDir = null;\r
87 \r
88 \r
89                 ArrayList lookupTypes = new ArrayList();\r
90                 ArrayList assemblies = new ArrayList();\r
91 \r
92                 ArrayList schemaNames = new ArrayList();\r
93                 ArrayList inferenceNames = new ArrayList();\r
94                 ArrayList elements = new ArrayList();\r
95                 string language = null;\r
96                 string namesp = null;\r
97                 string uri = null;\r
98                 string providerOption = null;\r
99                 CodeDomProvider provider;\r
100 \r
101                 public void Run (string[] args)\r
102                 {\r
103                         ArrayList unknownFiles = new ArrayList();\r
104                         bool generateClasses = false;\r
105                         bool readingFiles = true;\r
106                         bool schemasOptions = false;\r
107                         bool assemblyOptions = false;\r
108                         bool generateDataset = false;\r
109                         bool inference = false;\r
110 \r
111                         foreach (string arg in args)\r
112                         {\r
113                                 if (!arg.StartsWith ("--") && !arg.StartsWith ("/")) {\r
114                                         if (arg.EndsWith (".dll") || arg.EndsWith (".exe"))\r
115                                         {\r
116                                                 if (!readingFiles) throw new Exception (incorrectOrder);\r
117                                                 assemblies.Add (arg);\r
118                                                 assemblyOptions = true;\r
119                                                 continue;\r
120                                         }\r
121                                         else if (arg.EndsWith (".xsd"))\r
122                                         {\r
123                                                 if (!readingFiles) Error (incorrectOrder);\r
124                                                 schemaNames.Add (arg);\r
125                                                 schemasOptions = true;\r
126                                                 continue;\r
127                                         }\r
128                                         else if (arg.EndsWith (".xml"))\r
129                                         {\r
130                                                 if (generateClasses || generateDataset) Error (duplicatedParam);\r
131                                                 inferenceNames.Add (arg);\r
132                                                 inference = true;\r
133                                                 continue;\r
134                                         }\r
135                                         else //if (!arg.StartsWith ("/") && !arg.StartsWith ("-"))\r
136                                         {\r
137                                                 if (!readingFiles) Error (incorrectOrder);\r
138                                                 unknownFiles.Add (arg);\r
139                                                 continue;\r
140                                         }\r
141                                 }\r
142 \r
143                                 readingFiles = false;\r
144 \r
145                                 int i = arg.IndexOf (":");\r
146                                 if (i == -1) i = arg.Length;\r
147                                 string option = arg.Substring (1,i-1);\r
148                                 string param = (i<arg.Length-1) ? arg.Substring (i+1) : "";\r
149 \r
150                                 if (option == "classes" || option == "c")\r
151                                 {\r
152                                         if (generateClasses || generateDataset || inference) Error (duplicatedParam, option);\r
153                                         generateClasses = true;\r
154                                         schemasOptions = true;\r
155                                 }\r
156                                 else if (option == "dataset" || option == "d")\r
157                                 {\r
158                                         if (generateClasses || generateDataset || inference) Error (duplicatedParam, option);\r
159                                         generateDataset = true;\r
160                                         schemasOptions = true;\r
161                                 }\r
162                                 else if (option == "element" || option == "e")\r
163                                 {\r
164                                         elements.Add (param);\r
165                                         schemasOptions = true;\r
166                                 }\r
167                                 else if (option == "language" || option == "l")\r
168                                 {\r
169                                         if (provider != null) Error (duplicatedParam, option);\r
170                                         if (language != null) Error (duplicatedParam, option);\r
171                                         language = param;\r
172                                         schemasOptions = true;\r
173                                 }\r
174                                 else if (option == "namespace" || option == "n")\r
175                                 {\r
176                                         if (namesp != null) Error (duplicatedParam, option);\r
177                                         namesp = param;\r
178                                         schemasOptions = true;\r
179                                 }\r
180                                 else if (option == "outputdir" || option == "o")\r
181                                 {\r
182                                         if (outputDir != null) Error (duplicatedParam, option);\r
183                                         outputDir = param;\r
184                                 }\r
185                                 else if (option == "uri" || option == "u")\r
186                                 {\r
187                                         if (uri != null) Error (duplicatedParam, option);\r
188                                         uri = param;\r
189                                         schemasOptions = true;\r
190                                 }\r
191                                 else if (option == "type" || option == "t")\r
192                                 {\r
193                                         lookupTypes.Add (param);\r
194                                         assemblyOptions = true;\r
195                                 }\r
196                                 else if (option == "generator" || option == "g")\r
197                                 {\r
198                                         providerOption = param;\r
199                                 }\r
200                                 else if (option == "help" || option == "h")\r
201                                 {\r
202                                         Console.WriteLine (helpString);\r
203                                         return;\r
204                                 }\r
205                                 else\r
206                                         Error (unknownOption, option);\r
207                         }\r
208 \r
209                         if (!schemasOptions && !assemblyOptions && !inference)\r
210                                 Error (invalidParams);\r
211 \r
212                         if (schemasOptions && assemblyOptions)\r
213                                 Error (incompatibleArgs);\r
214 \r
215                         if (assemblies.Count > 1)\r
216                                 Error (tooManyAssem);\r
217 \r
218                         if (outputDir == null) outputDir = ".";\r
219 \r
220                         if (language != null) {\r
221                                 switch (language) {\r
222                                 case "CS":\r
223                                         provider = new CSharpCodeProvider ();\r
224                                         break;\r
225                                 case "VB":\r
226                                         provider = new VBCodeProvider ();\r
227                                         break;\r
228                                 default:\r
229                                         Error (languageNotSupported, language);\r
230                                         break;\r
231                                 }\r
232                         }\r
233 \r
234                         if (providerOption != null) {\r
235                                 string param = providerOption;\r
236                                 string typename;\r
237                                 Type generatorType;\r
238                                 int comma = param.IndexOf (',');\r
239                                 if (comma < 0) {\r
240                                         typename = param;\r
241                                         generatorType = Type.GetType (param);\r
242                                 } else {\r
243                                         typename = param.Substring (0, comma);\r
244                                         string asmName = param.Substring (comma + 1);\r
245 #if NET_1_1\r
246                                         Assembly asm = Assembly.LoadFile (asmName);\r
247 #else\r
248                                         Assembly asm = Assembly.LoadFrom (asmName);\r
249 #endif\r
250                                         if (asm == null)\r
251                                                 Error (generatorAssemblyNotFound, asmName);\r
252                                         generatorType = asm.GetType (typename);\r
253                                 }\r
254                                 if (generatorType == null)\r
255                                         Error (generatorTypeNotFound, typename);\r
256                                 if (!generatorType.IsSubclassOf (typeof (CodeDomProvider)))\r
257                                         Error (generatorTypeIsNotCodeGenerator, typename);\r
258                                 try {\r
259                                         provider = (CodeDomProvider) Activator.CreateInstance (generatorType, null);\r
260                                 } catch (Exception ex) {\r
261                                         Error (generatorThrewException, param);\r
262                                 }\r
263                                 Console.WriteLine ("Loaded custom generator type " + param + " .");\r
264                         }\r
265                         if (provider == null)\r
266                                 provider = new CSharpCodeProvider ();\r
267 \r
268                         if (schemasOptions)\r
269                         {\r
270                                 if (!generateClasses && !generateDataset)\r
271                                         Error (missingOutputForXsdInput);\r
272                                 schemaNames.AddRange (unknownFiles);\r
273                                 if (generateClasses)\r
274                                         GenerateClasses ();\r
275                                 else if (generateDataset)\r
276                                         GenerateDataset ();\r
277                         }\r
278                         else if (inference)\r
279                         {\r
280                                 foreach (string xmlfile in inferenceNames) {\r
281                                         string genFile = Path.Combine (outputDir, Path.GetFileNameWithoutExtension (xmlfile) + ".xsd");\r
282                                         DataSet ds = new DataSet ();\r
283                                         ds.InferXmlSchema (xmlfile, null);\r
284                                         ds.WriteXmlSchema (genFile);\r
285                                         Console.WriteLine ("Written file " + genFile);\r
286                                 }\r
287                         }\r
288                         else\r
289                         {\r
290                                 assemblies.AddRange (unknownFiles);\r
291                                 GenerateSchemas ();\r
292                         }\r
293                 }\r
294 \r
295                 public void GenerateSchemas ()\r
296                 {\r
297                         Assembly assembly = null;\r
298                         try\r
299                         {\r
300                                 assembly = Assembly.LoadFrom ((string) assemblies [0]);\r
301                         }\r
302                         catch (Exception ex)\r
303                         {\r
304                                 Error (errLoadAssembly, ex.Message);\r
305                         }\r
306                         \r
307                         Type[] types;\r
308                         \r
309                         if (lookupTypes.Count > 0)\r
310                         {\r
311                                 types = new Type [lookupTypes.Count];\r
312                                 for (int n=0; n<lookupTypes.Count; n++)\r
313                                 {\r
314                                         Type t = assembly.GetType ((string)lookupTypes[n]);\r
315                                         if (t == null) Error (typeNotFound, (string)lookupTypes[n]);\r
316                                         types[n] = t;\r
317                                 }\r
318                         }\r
319                         else\r
320                                 types = assembly.GetExportedTypes ();\r
321 \r
322                         XmlReflectionImporter ri = new XmlReflectionImporter ();\r
323                         XmlSchemas schemas = new XmlSchemas ();\r
324                         XmlSchemaExporter sx = new XmlSchemaExporter (schemas);\r
325 \r
326                         foreach (Type type in types)\r
327                         {\r
328                                 XmlTypeMapping tm = ri.ImportTypeMapping (type);\r
329                                 sx.ExportTypeMapping (tm);\r
330                         }\r
331 \r
332                         if (schemas.Count == 1)\r
333                         {\r
334                                 string fileName = Path.Combine (outputDir, "schema.xsd");\r
335                                 WriteSchema (fileName, schemas [0]);\r
336                         }\r
337                         else\r
338                         {\r
339                                 for (int n=0; n<schemas.Count; n++)\r
340                                 {\r
341                                         string fileName = Path.Combine (outputDir, "schema" + n + ".xsd");\r
342                                         WriteSchema (fileName, schemas [n]);\r
343                                 }\r
344                         }\r
345                 }\r
346 \r
347                 void WriteSchema (string fileName, XmlSchema schema)\r
348                 {\r
349                         StreamWriter sw = new StreamWriter (fileName);\r
350                         schema.Write (sw);\r
351                         sw.Close ();\r
352                         Console.WriteLine ("Written file " + fileName);\r
353                 }\r
354                 \r
355                 public void GenerateClasses ()\r
356                 {\r
357                         if (namesp == null) namesp = "Schemas";\r
358                         if (uri == null) uri = "";\r
359                         string targetFile = "";\r
360 \r
361                         XmlSchemas schemas = new XmlSchemas();\r
362                         foreach (string fileName in schemaNames)\r
363                         {\r
364                                 StreamReader sr = new StreamReader (fileName);\r
365                                 schemas.Add (XmlSchema.Read (sr, null));\r
366                                 sr.Close ();\r
367 \r
368                                 if (targetFile == "") targetFile = Path.GetFileNameWithoutExtension (fileName);\r
369                                 else targetFile += "_" + Path.GetFileNameWithoutExtension (fileName);\r
370                         }\r
371 \r
372                         targetFile += "." + provider.FileExtension;\r
373 \r
374                         CodeCompileUnit cunit = new CodeCompileUnit ();\r
375                         CodeNamespace codeNamespace = new CodeNamespace (namesp);\r
376                         cunit.Namespaces.Add (codeNamespace);\r
377                         codeNamespace.Comments.Add (new CodeCommentStatement ("\nThis source code was auto-generated by MonoXSD\n"));\r
378 \r
379                         // Locate elements to generate\r
380 \r
381                         ArrayList qnames = new ArrayList ();\r
382                         if (elements.Count > 0)\r
383                         {\r
384                                 foreach (string name in elements)\r
385                                         qnames.Add (new XmlQualifiedName (name, uri));\r
386                         }\r
387                         else\r
388                         {\r
389                                 foreach (XmlSchema schema in schemas) {\r
390                                         if (!schema.IsCompiled) schema.Compile (null);\r
391                                         foreach (XmlSchemaObject ob in schema.Items)\r
392                                                 if (ob is XmlSchemaElement)\r
393                                                         qnames.Add (((XmlSchemaElement)ob).QualifiedName);\r
394                                 }\r
395                         }\r
396 \r
397                         // Import schemas and generate the class model\r
398 \r
399                         XmlSchemaImporter importer = new XmlSchemaImporter (schemas);\r
400                         XmlCodeExporter sx = new XmlCodeExporter (codeNamespace, cunit);\r
401 \r
402                         ArrayList maps = new ArrayList();\r
403 \r
404                         foreach (XmlQualifiedName qname in qnames)\r
405                         {\r
406                                 XmlTypeMapping tm = importer.ImportTypeMapping (qname);\r
407                                 if (tm != null) maps.Add (tm);\r
408                         }\r
409                         \r
410                         foreach (XmlTypeMapping tm in maps)\r
411                         {\r
412                                 sx.ExportTypeMapping (tm);\r
413                         }\r
414 \r
415                         // Generate the code\r
416                         \r
417                         ICodeGenerator gen = provider.CreateGenerator();\r
418 \r
419                         string genFile = Path.Combine (outputDir, targetFile);\r
420                         StreamWriter sw = new StreamWriter(genFile, false);\r
421                         gen.GenerateCodeFromCompileUnit (cunit, sw, new CodeGeneratorOptions());\r
422                         sw.Close();\r
423 \r
424                         Console.WriteLine ("Written file " + genFile);\r
425                 }\r
426 \r
427                 public void GenerateDataset ()\r
428                 {\r
429                         if (namesp == null) namesp = "Schemas";\r
430                         if (uri == null) uri = "";\r
431                         string targetFile = "";\r
432 \r
433                         DataSet dataset = new DataSet ();\r
434                         foreach (string fileName in schemaNames)\r
435                         {\r
436                                 dataset.ReadXmlSchema (fileName);\r
437 \r
438                                 if (targetFile == "") targetFile = Path.GetFileNameWithoutExtension (fileName);\r
439                                 else targetFile += "_" + Path.GetFileNameWithoutExtension (fileName);\r
440                         }\r
441 \r
442                         targetFile += "." + provider.FileExtension;\r
443 \r
444                         CodeCompileUnit cunit = new CodeCompileUnit ();\r
445                         CodeNamespace codeNamespace = new CodeNamespace (namesp);\r
446                         cunit.Namespaces.Add (codeNamespace);\r
447                         codeNamespace.Comments.Add (new CodeCommentStatement ("\nThis source code was auto-generated by MonoXSD\n"));\r
448 \r
449                         // Generate the code\r
450                         \r
451                         ICodeGenerator gen = provider.CreateGenerator ();\r
452 \r
453                         TypedDataSetGenerator.Generate (dataset, codeNamespace, gen);\r
454 \r
455                         string genFile = Path.Combine (outputDir, targetFile);\r
456                         StreamWriter sw = new StreamWriter(genFile, false);\r
457                         gen.GenerateCodeFromCompileUnit (cunit, sw, new CodeGeneratorOptions());\r
458                         sw.Close();\r
459 \r
460                         Console.WriteLine ("Written file " + genFile);\r
461                 }\r
462 \r
463                 public void Error (string msg)\r
464                 {\r
465                         throw new Exception (msg);\r
466                 }\r
467 \r
468                 public void Error (string msg, string param)\r
469                 {\r
470                         throw new Exception (string.Format(msg,param));\r
471                 }\r
472         }\r
473 }\r