Merge pull request #3956 from directhex/install-btls-certs-in-jenkins
[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, or type name of custom CodeDomProvider\n" +\r
44                         "                      to use for the generated code.\n" +\r
45                         "                      Shorthand specifiers are: \"CS\" (C#) and \"VB\" (VB.NET).\n" +\r
46                         "                      For type name, assembly qualified name is required.\n" +\r
47                         "   /g /generator:TYPE Code Generator type name, followed by ','\n" + \r
48                         "                      and assembly file name.\n" +\r
49                         "   /o /outputdir:PATH The directory where to generate the code or schemas.\n" +\r
50                         "   /n /namespace:NAME Namespace for the generated code.\n" +\r
51                         "   /t /type:NAME      Type for which to generate an xml schema.\n" +\r
52                         "                      Multiple types can be specified.\n" +\r
53                         "   /h /help           Output this help.\n";\r
54 \r
55                 static readonly string incorrectOrder = "Options must be specified after Assembly or schema file names";\r
56                 static readonly string duplicatedParam = "The option {0} cannot be specified more that once";\r
57                 static readonly string unknownOption = "Unknown option {0}";\r
58                 static readonly string incompatibleArgs = "Cannot mix options for generating schemas and for generatic classes";\r
59                 static readonly string invalidParams = "Invalid parameters";\r
60                 static readonly string tooManyAssem = "Only one assembly name can be specified";\r
61                 static readonly string errLoadAssembly = "Could not load assembly: {0}";\r
62                 static readonly string typeNotFound = "Type {0} not found in the specified assembly";\r
63                 static readonly string languageNotSupported = "The language {0} is not supported";\r
64                 static readonly string missingOutputForXsdInput = "Can only generate one of classes or datasets.";\r
65                 static readonly string generatorAssemblyNotFound = "Could not load code provider assembly file: {0}";\r
66                 static readonly string generatorTypeNotFound = "Could not find specified code provider type: {0}";\r
67                 static readonly string generatorTypeIsNotCodeGenerator = "Specified code provider type was not CodeDomProvider: {0}";\r
68                 static readonly string generatorThrewException = "Specified CodeDomProvider raised an error while creating its instance: {0}";\r
69 \r
70                 static void Main (string [] args)\r
71                 {\r
72                         if (args.Length < 1) {\r
73                                 Console.WriteLine (helpString);\r
74                                 Environment.Exit (0);\r
75                         }\r
76 \r
77                         try\r
78                         {\r
79                                 new Driver().Run (args);\r
80                         }\r
81                         catch (ApplicationException ex)\r
82                         {\r
83                                 Console.WriteLine (ex.Message);\r
84                         }\r
85                         catch (Exception ex)\r
86                         {\r
87                                 Console.WriteLine (ex);\r
88                         }\r
89                 }\r
90 \r
91                 string outputDir = null;\r
92 \r
93 \r
94                 ArrayList lookupTypes = new ArrayList();\r
95                 ArrayList assemblies = new ArrayList();\r
96 \r
97                 ArrayList schemaNames = new ArrayList();\r
98                 ArrayList inferenceNames = new ArrayList();\r
99                 ArrayList elements = new ArrayList();\r
100                 string language = null;\r
101                 string namesp = null;\r
102                 string uri = null;\r
103                 string providerOption = null;\r
104                 CodeDomProvider provider;\r
105 \r
106                 public void Run (string[] args)\r
107                 {\r
108                         ArrayList unknownFiles = new ArrayList();\r
109                         bool generateClasses = false;\r
110                         bool readingFiles = true;\r
111                         bool schemasOptions = false;\r
112                         bool assemblyOptions = false;\r
113                         bool generateDataset = false;\r
114                         bool inference = false;\r
115 \r
116                         foreach (string arg in args)\r
117                         {\r
118                                 if (!arg.StartsWith ("--") && !arg.StartsWith ("/") ||\r
119                                         (arg.StartsWith ("/") && arg.IndexOfAny (Path.InvalidPathChars) == -1)\r
120                                         ) \r
121                                 {\r
122                                         if ((arg.EndsWith (".dll") || arg.EndsWith (".exe")) && !arg.Substring (1).StartsWith ("generator:") && !arg.Substring (1).StartsWith ("g:"))\r
123                                         {\r
124                                                 if (!readingFiles) throw new ApplicationException (incorrectOrder);\r
125                                                 assemblies.Add (arg);\r
126                                                 assemblyOptions = true;\r
127                                                 continue;\r
128                                         }\r
129                                         else if (arg.EndsWith (".xsd"))\r
130                                         {\r
131                                                 if (!readingFiles) Error (incorrectOrder);\r
132                                                 schemaNames.Add (arg);\r
133                                                 schemasOptions = true;\r
134                                                 continue;\r
135                                         }\r
136                                         else if (arg.EndsWith (".xml"))\r
137                                         {\r
138                                                 if (generateClasses || generateDataset) Error (duplicatedParam);\r
139                                                 inferenceNames.Add (arg);\r
140                                                 inference = true;\r
141                                                 continue;\r
142                                         }\r
143                                         else if (!arg.StartsWith ("/"))\r
144                                         {\r
145                                                 if (!readingFiles) Error (incorrectOrder);\r
146                                                 unknownFiles.Add (arg);\r
147                                                 continue;\r
148                                         }\r
149                                 }\r
150 \r
151                                 readingFiles = false;\r
152 \r
153                                 int i = arg.IndexOf (":");\r
154                                 if (i == -1) i = arg.Length;\r
155                                 string option = arg.Substring (1,i-1);\r
156                                 string param = (i<arg.Length-1) ? arg.Substring (i+1) : "";\r
157 \r
158                                 if (option == "classes" || option == "c")\r
159                                 {\r
160                                         if (generateClasses || generateDataset || inference) Error (duplicatedParam, option);\r
161                                         generateClasses = true;\r
162                                         schemasOptions = true;\r
163                                 }\r
164                                 else if (option == "dataset" || option == "d")\r
165                                 {\r
166                                         if (generateClasses || generateDataset || inference) Error (duplicatedParam, option);\r
167                                         generateDataset = true;\r
168                                         schemasOptions = true;\r
169                                 }\r
170                                 else if (option == "element" || option == "e")\r
171                                 {\r
172                                         elements.Add (param);\r
173                                         schemasOptions = true;\r
174                                 }\r
175                                 else if (option == "language" || option == "l")\r
176                                 {\r
177                                         if (provider != null) Error (duplicatedParam, option);\r
178                                         if (language != null) Error (duplicatedParam, option);\r
179                                         language = param;\r
180                                         schemasOptions = true;\r
181                                 }\r
182                                 else if (option == "namespace" || option == "n")\r
183                                 {\r
184                                         if (namesp != null) Error (duplicatedParam, option);\r
185                                         namesp = param;\r
186                                         schemasOptions = true;\r
187                                 }\r
188                                 else if (option == "outputdir" || option == "o")\r
189                                 {\r
190                                         if (outputDir != null) Error (duplicatedParam, option);\r
191                                         outputDir = param;\r
192                                 }\r
193                                 else if (option == "uri" || option == "u")\r
194                                 {\r
195                                         if (uri != null) Error (duplicatedParam, option);\r
196                                         uri = param;\r
197                                         schemasOptions = true;\r
198                                 }\r
199                                 else if (option == "type" || option == "t")\r
200                                 {\r
201                                         lookupTypes.Add (param);\r
202                                         assemblyOptions = true;\r
203                                 }\r
204                                 else if (option == "generator" || option == "g")\r
205                                 {\r
206                                         providerOption = param;\r
207                                 }\r
208                                 else if (option == "help" || option == "h")\r
209                                 {\r
210                                         Console.WriteLine (helpString);\r
211                                         return;\r
212                                 }\r
213                                 else if (option == "nologo")\r
214                                 {\r
215                                         // ignore, since we do not output a logo anyway\r
216                                 }\r
217                                 else\r
218                                         Error (unknownOption, option);\r
219                         }\r
220 \r
221                         if (!schemasOptions && !assemblyOptions && !inference)\r
222                                 Error (invalidParams);\r
223 \r
224                         if (schemasOptions && assemblyOptions)\r
225                                 Error (incompatibleArgs);\r
226 \r
227                         if (assemblies.Count > 1)\r
228                                 Error (tooManyAssem);\r
229 \r
230                         if (outputDir == null) outputDir = ".";\r
231 \r
232                         string typename = null;\r
233                         Type generatorType = null;\r
234 \r
235                         if (language != null) {\r
236                                 switch (language) {\r
237                                 case "CS":\r
238                                         provider = new CSharpCodeProvider ();\r
239                                         break;\r
240                                 case "VB":\r
241                                         provider = new VBCodeProvider ();\r
242                                         break;\r
243                                 default:\r
244                                         typename = StripQuot (language);\r
245 \r
246                                         generatorType = Type.GetType (typename);\r
247                                         if (generatorType == null)\r
248                                                 Error (generatorTypeNotFound, typename);\r
249                                         break;\r
250                                 }\r
251                         }\r
252 \r
253                         if (providerOption != null) {\r
254                                 string param = providerOption;\r
255                                 int comma = param.IndexOf (',');\r
256                                 if (comma < 0) {\r
257                                         typename = StripQuot (param);\r
258                                         generatorType = Type.GetType (param);\r
259                                 } else {\r
260                                         typename = param.Substring (0, comma);\r
261                                         string asmName = param.Substring (comma + 1);\r
262                                         Assembly asm = Assembly.LoadFile (asmName);\r
263                                         if (asm == null)\r
264                                                 Error (generatorAssemblyNotFound, asmName);\r
265                                         generatorType = asm.GetType (typename);\r
266                                 }\r
267                                 if (generatorType == null)\r
268                                         Error (generatorTypeNotFound, typename);\r
269                         }\r
270                         if (generatorType != null) {\r
271                                 if (!generatorType.IsSubclassOf (typeof (CodeDomProvider)))\r
272                                         Error (generatorTypeIsNotCodeGenerator, typename);\r
273                                 try {\r
274                                         provider = (CodeDomProvider) Activator.CreateInstance (generatorType, null);\r
275                                 } catch (Exception ex) {\r
276                                         Error (generatorThrewException, generatorType.AssemblyQualifiedName.ToString () + " --> " + ex.Message);\r
277                                 }\r
278                                 Console.WriteLine ("Loaded custom generator type " + generatorType + " .");\r
279                         }\r
280                         if (provider == null)\r
281                                 provider = new CSharpCodeProvider ();\r
282 \r
283                         if (schemasOptions)\r
284                         {\r
285                                 if (!generateClasses && !generateDataset)\r
286                                         Error (missingOutputForXsdInput);\r
287                                 schemaNames.AddRange (unknownFiles);\r
288                                 if (generateClasses)\r
289                                         GenerateClasses ();\r
290                                 else if (generateDataset)\r
291                                         GenerateDataset ();\r
292                         }\r
293                         else if (inference)\r
294                         {\r
295                                 foreach (string xmlfile in inferenceNames) {\r
296                                         string genFile = Path.Combine (outputDir, Path.GetFileNameWithoutExtension (xmlfile) + ".xsd");\r
297                                         DataSet ds = new DataSet ();\r
298                                         ds.InferXmlSchema (xmlfile, null);\r
299                                         ds.WriteXmlSchema (genFile);\r
300                                         Console.WriteLine ("Written file " + genFile);\r
301                                 }\r
302                         }\r
303                         else\r
304                         {\r
305                                 assemblies.AddRange (unknownFiles);\r
306                                 GenerateSchemas ();\r
307                         }\r
308                 }\r
309 \r
310                 public void GenerateSchemas ()\r
311                 {\r
312                         Assembly assembly = null;\r
313                         try\r
314                         {\r
315                                 assembly = Assembly.LoadFrom ((string) assemblies [0]);\r
316                         }\r
317                         catch (Exception ex)\r
318                         {\r
319                                 Error (errLoadAssembly, ex.Message);\r
320                         }\r
321                         \r
322                         Type[] types;\r
323                         \r
324                         if (lookupTypes.Count > 0)\r
325                         {\r
326                                 types = new Type [lookupTypes.Count];\r
327                                 for (int n=0; n<lookupTypes.Count; n++)\r
328                                 {\r
329                                         Type t = assembly.GetType ((string)lookupTypes[n]);\r
330                                         if (t == null) Error (typeNotFound, (string)lookupTypes[n]);\r
331                                         types[n] = t;\r
332                                 }\r
333                         }\r
334                         else\r
335                                 types = assembly.GetExportedTypes ();\r
336 \r
337                         XmlReflectionImporter ri = new XmlReflectionImporter ();\r
338                         XmlSchemas schemas = new XmlSchemas ();\r
339                         XmlSchemaExporter sx = new XmlSchemaExporter (schemas);\r
340 \r
341                         foreach (Type type in types)\r
342                         {\r
343                                 XmlTypeMapping tm = ri.ImportTypeMapping (type);\r
344                                 sx.ExportTypeMapping (tm);\r
345                         }\r
346 \r
347                         if (schemas.Count == 1)\r
348                         {\r
349                                 string fileName = Path.Combine (outputDir, "schema.xsd");\r
350                                 WriteSchema (fileName, schemas [0]);\r
351                         }\r
352                         else\r
353                         {\r
354                                 for (int n=0; n<schemas.Count; n++)\r
355                                 {\r
356                                         string fileName = Path.Combine (outputDir, "schema" + n + ".xsd");\r
357                                         WriteSchema (fileName, schemas [n]);\r
358                                 }\r
359                         }\r
360                 }\r
361 \r
362                 void WriteSchema (string fileName, XmlSchema schema)\r
363                 {\r
364                         StreamWriter sw = new StreamWriter (fileName);\r
365                         schema.Write (sw);\r
366                         sw.Close ();\r
367                         Console.WriteLine ("Written file " + fileName);\r
368                 }\r
369                 \r
370                 public void GenerateClasses ()\r
371                 {\r
372                         if (namesp == null) namesp = "Schemas";\r
373                         if (uri == null) uri = "";\r
374                         string targetFile = "";\r
375 \r
376                         XmlSchemas schemas = new XmlSchemas();\r
377                         foreach (string fileName in schemaNames)\r
378                         {\r
379                                 StreamReader sr = new StreamReader (fileName);\r
380                                 schemas.Add (XmlSchema.Read (sr, new ValidationEventHandler (HandleValidationError)));\r
381                                 sr.Close ();\r
382 \r
383                                 if (targetFile == "") targetFile = Path.GetFileNameWithoutExtension (fileName);\r
384                                 else targetFile += "_" + Path.GetFileNameWithoutExtension (fileName);\r
385                         }\r
386 \r
387                         targetFile += "." + provider.FileExtension;\r
388 \r
389                         CodeCompileUnit cunit = new CodeCompileUnit ();\r
390                         CodeNamespace codeNamespace = new CodeNamespace (namesp);\r
391                         cunit.Namespaces.Add (codeNamespace);\r
392                         codeNamespace.Comments.Add (new CodeCommentStatement ("\nThis source code was auto-generated by MonoXSD\n"));\r
393 \r
394                         // Locate elements to generate\r
395 \r
396                         ArrayList qnames = new ArrayList ();\r
397                         if (elements.Count > 0)\r
398                         {\r
399                                 foreach (string name in elements)\r
400                                         qnames.Add (new XmlQualifiedName (name, uri));\r
401                         }\r
402                         else\r
403                         {\r
404                                 foreach (XmlSchema schema in schemas) {\r
405                                         if (!schema.IsCompiled) schema.Compile (new ValidationEventHandler (HandleValidationError));\r
406                                         foreach (XmlSchemaElement el in schema.Elements.Values)\r
407                                                 if (!qnames.Contains (el.QualifiedName))\r
408                                                         qnames.Add (el.QualifiedName);\r
409                                 }\r
410                         }\r
411 \r
412                         // Import schemas and generate the class model\r
413 \r
414                         XmlSchemaImporter importer = new XmlSchemaImporter (schemas);\r
415                         XmlCodeExporter sx = new XmlCodeExporter (codeNamespace, cunit);\r
416 \r
417                         ArrayList maps = new ArrayList();\r
418 \r
419                         foreach (XmlQualifiedName qname in qnames)\r
420                         {\r
421                                 XmlTypeMapping tm = importer.ImportTypeMapping (qname);\r
422                                 if (tm != null) maps.Add (tm);\r
423                         }\r
424                         \r
425                         foreach (XmlTypeMapping tm in maps)\r
426                         {\r
427                                 sx.ExportTypeMapping (tm);\r
428                         }\r
429 \r
430                         // Generate the code\r
431                         \r
432                         ICodeGenerator gen = provider.CreateGenerator();\r
433 \r
434                         string genFile = Path.Combine (outputDir, targetFile);\r
435                         StreamWriter sw = new StreamWriter(genFile, false);\r
436                         gen.GenerateCodeFromCompileUnit (cunit, sw, new CodeGeneratorOptions());\r
437                         sw.Close();\r
438 \r
439                         Console.WriteLine ("Written file " + genFile);\r
440                 }\r
441 \r
442                 public void GenerateDataset ()\r
443                 {\r
444                         if (namesp == null) namesp = "";\r
445                         if (uri == null) uri = "";\r
446                         string targetFile = "";\r
447 \r
448                         DataSet dataset = new DataSet ();\r
449                         foreach (string fileName in schemaNames)\r
450                         {\r
451                                 dataset.ReadXmlSchema (fileName);\r
452 \r
453                                 if (targetFile == "") targetFile = Path.GetFileNameWithoutExtension (fileName);\r
454                                 else targetFile += "_" + Path.GetFileNameWithoutExtension (fileName);\r
455                         }\r
456 \r
457                         targetFile += "." + provider.FileExtension;\r
458 \r
459                         CodeCompileUnit cunit = new CodeCompileUnit ();\r
460                         CodeNamespace codeNamespace = new CodeNamespace (namesp);\r
461                         cunit.Namespaces.Add (codeNamespace);\r
462                         codeNamespace.Comments.Add (new CodeCommentStatement ("\nThis source code was auto-generated by MonoXSD\n"));\r
463 \r
464                         // Generate the code\r
465                         \r
466                         ICodeGenerator gen = provider.CreateGenerator ();\r
467 \r
468                         TypedDataSetGenerator.Generate (dataset, codeNamespace, gen);\r
469 \r
470                         string genFile = Path.Combine (outputDir, targetFile);\r
471                         StreamWriter sw = new StreamWriter(genFile, false);\r
472                         gen.GenerateCodeFromCompileUnit (cunit, sw, new CodeGeneratorOptions());\r
473                         sw.Close();\r
474 \r
475                         Console.WriteLine ("Written file " + genFile);\r
476                 }\r
477 \r
478                 void HandleValidationError (object o, ValidationEventArgs e)\r
479                 {\r
480                         Console.WriteLine ("{0}: {1} {2}",\r
481                                 e.Severity == XmlSeverityType.Error ? "Error" : "Warning",\r
482                                 e.Message,\r
483                                 e.Exception != null ? e.Exception.Message : null);\r
484                 }\r
485 \r
486                 public void Error (string msg)\r
487                 {\r
488                         throw new ApplicationException (msg);\r
489                 }\r
490 \r
491                 public void Error (string msg, string param)\r
492                 {\r
493                         throw new ApplicationException (string.Format(msg,param));\r
494                 }\r
495 \r
496                 private string StripQuot (string input)\r
497                 {\r
498                         if (input.Length < 2)\r
499                                 return input;\r
500                         if (input [0] == '"' && input [input.Length -1] == '"' ||\r
501                                 input [0] == '\'' && input [input.Length - 1] == '\'')\r
502                                 return input.Substring (1, input.Length - 2);\r
503                         else\r
504                                 return language;\r
505                 }\r
506         }\r
507 }\r