* NewMonoXSD.cs: Compile the loaded schemas if they are not already compiled.
[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 /// Author: Duncan Mak (duncan@ximian.com)\r
5 ///         Lluis Sanchez Gual (lluis@ximian.com)\r
6 ///\r
7 /// Copyright (C) 2003, Duncan Mak,\r
8 ///                     Ximian, Inc.\r
9 ///\r
10 \r
11 using System;\r
12 using System.Collections;\r
13 using System.IO;\r
14 using System.Reflection;\r
15 using System.Xml;\r
16 using System.Xml.Schema;\r
17 using System.Xml.Serialization;\r
18 using System.CodeDom;\r
19 using System.CodeDom.Compiler;\r
20 using Microsoft.CSharp;\r
21 \r
22 namespace Mono.Util {\r
23 \r
24         public class Driver\r
25         {\r
26                 public static readonly string helpString =\r
27                         "xsd.exe - a utility for generating schema or class files\n\n" +\r
28                         "xsd.exe <schema>.xsd /classes [/element:NAME] [/language:NAME]\n" +\r
29                         "            [/namespace:NAME] [/outputdir:PATH] [/uri:NAME]\n\n" +\r
30                         "xsd.exe <assembly>.dll|<assembly>.exe [/outputdir:PATH] [/type:NAME]\n\n" +\r
31                         "   /c /classes        Generate classes for the specified schema.\n" +\r
32                         "   /e /element:NAME   Element from schema to generate code for.\n" +\r
33                         "                      Multiple elements can be specified.\n" +\r
34                         "   /u /uri:NAME       Namespace uri of the elements to generate code for.\n" +\r
35                         "   /l /language:NAME  The language to use for the generated code.\n" +\r
36                         "                      Currently, the only supported language is CS (C#).\n" +\r
37                         "   /o /outputdir:PATH The directory where to generate the code or schemas.\n" +\r
38                         "   /n /namespace:NAME Namespace for the generated code.\n" +\r
39                         "   /t /type:NAME      Type for which to generate an xml schema.\n" +\r
40                         "                      Multiple types can be specified.\n" +\r
41                         "   /h /help           Output this help.\n";\r
42 \r
43                 static readonly string incorrectOrder = "Options must be specified after Assembly or schema file names";\r
44                 static readonly string duplicatedParam = "The option {0} cannot be specified more that once";\r
45                 static readonly string unknownOption = "Unknown option {0}";\r
46                 static readonly string incompatibleArgs = "Cannot mix options for generating schemas and for generatic classes";\r
47                 static readonly string invalidParams = "Invalid parameters";\r
48                 static readonly string tooManyAssem = "Only one assembly name can be specified";\r
49                 static readonly string errLoadAssembly = "Could not load assembly: {0}";\r
50                 static readonly string typeNotFound = "Type {0} not found in the specified assembly";\r
51                 static readonly string languageNotSupported = "The language {0} is not supported";\r
52                 \r
53 \r
54                 static void Main (string [] args)\r
55                 {\r
56                         if (args.Length < 1) {\r
57                                 Console.WriteLine (helpString);\r
58                                 Environment.Exit (0);\r
59                         }\r
60 \r
61                         try\r
62                         {\r
63                                 new Driver().Run (args);\r
64                         }\r
65                         catch (Exception ex)\r
66                         {\r
67                                 Console.WriteLine (ex.Message);\r
68                                 Console.WriteLine (ex);\r
69                         }\r
70                 }\r
71 \r
72                 string outputDir = null;\r
73 \r
74 \r
75                 ArrayList lookupTypes = new ArrayList();\r
76                 ArrayList assemblies = new ArrayList();\r
77 \r
78                 ArrayList schemaNames = new ArrayList();\r
79                 ArrayList elements = new ArrayList();\r
80                 string language = null;\r
81                 string namesp = null;\r
82                 string uri = null;\r
83 \r
84                 public void Run (string[] args)\r
85                 {\r
86                         ArrayList unknownFiles = new ArrayList();\r
87                         bool generateClasses = false;\r
88                         bool readingFiles = true;\r
89                         bool schemasOptions = false;\r
90                         bool assemblyOptions = false;\r
91 \r
92                         foreach (string arg in args)\r
93                         {\r
94                                 if (arg.EndsWith (".dll") || arg.EndsWith (".exe"))\r
95                                 {\r
96                                         if (!readingFiles) throw new Exception (incorrectOrder);\r
97                                         assemblies.Add (arg);\r
98                                         assemblyOptions = true;\r
99                                         continue;\r
100                                 }\r
101                                 else if (arg.EndsWith (".xsd"))\r
102                                 {\r
103                                         if (!readingFiles) Error (incorrectOrder);\r
104                                         schemaNames.Add (arg);\r
105                                         schemasOptions = true;\r
106                                         continue;\r
107                                 }\r
108                                 else if (!arg.StartsWith ("/") && !arg.StartsWith ("-"))\r
109                                 {\r
110                                         if (!readingFiles) Error (incorrectOrder);\r
111                                         unknownFiles.Add (arg);\r
112                                         continue;\r
113                                 }\r
114 \r
115                                 readingFiles = false;\r
116 \r
117                                 int i = arg.IndexOf (":");\r
118                                 if (i == -1) i = arg.Length;\r
119                                 string option = arg.Substring (1,i-1);\r
120                                 string param = (i<arg.Length-1) ? arg.Substring (i+1) : "";\r
121 \r
122                                 if (option == "classes" || option == "c")\r
123                                 {\r
124                                         if (generateClasses) Error (duplicatedParam, option);\r
125                                         generateClasses = true;\r
126                                         schemasOptions = true;\r
127                                 }\r
128                                 else if (option == "element" || option == "e")\r
129                                 {\r
130                                         elements.Add (param);\r
131                                         schemasOptions = true;\r
132                                 }\r
133                                 else if (option == "language" || option == "l")\r
134                                 {\r
135                                         if (language != null) Error (duplicatedParam, option);\r
136                                         language = param;\r
137                                         schemasOptions = true;\r
138                                 }\r
139                                 else if (option == "namespace" || option == "n")\r
140                                 {\r
141                                         if (namesp != null) Error (duplicatedParam, option);\r
142                                         namesp = param;\r
143                                         schemasOptions = true;\r
144                                 }\r
145                                 else if (option == "outputdir" || option == "o")\r
146                                 {\r
147                                         if (outputDir != null) Error (duplicatedParam, option);\r
148                                         outputDir = param;\r
149                                 }\r
150                                 else if (option == "uri" || option == "u")\r
151                                 {\r
152                                         if (uri != null) Error (duplicatedParam, option);\r
153                                         uri = param;\r
154                                         schemasOptions = true;\r
155                                 }\r
156                                 else if (option == "type" || option == "t")\r
157                                 {\r
158                                         lookupTypes.Add (param);\r
159                                         assemblyOptions = true;\r
160                                 }\r
161                                 else if (option == "help" || option == "h")\r
162                                 {\r
163                                         Console.WriteLine (helpString);\r
164                                         return;\r
165                                 }\r
166                                 else\r
167                                         Error (unknownOption, option);\r
168                         }\r
169 \r
170                         if (!schemasOptions && !assemblyOptions)\r
171                                 Error (invalidParams);\r
172 \r
173                         if (schemasOptions && assemblyOptions)\r
174                                 Error (incompatibleArgs);\r
175 \r
176                         if (assemblies.Count > 1)\r
177                                 Error (tooManyAssem);\r
178 \r
179                         if (outputDir == null) outputDir = ".";\r
180                                 \r
181                         if (schemasOptions)\r
182                         {\r
183                                 schemaNames.AddRange (unknownFiles);\r
184                                 GenerateClasses ();\r
185                         }\r
186                         else\r
187                         {\r
188                                 assemblies.AddRange (unknownFiles);\r
189                                 GenerateSchemas ();\r
190                         }\r
191                 }\r
192 \r
193                 public void GenerateSchemas ()\r
194                 {\r
195                         Assembly assembly = null;\r
196                         try\r
197                         {\r
198                                 assembly = Assembly.LoadFrom ((string) assemblies [0]);\r
199                         }\r
200                         catch (Exception ex)\r
201                         {\r
202                                 Error (errLoadAssembly, ex.Message);\r
203                         }\r
204                         \r
205                         Type[] types;\r
206                         \r
207                         if (lookupTypes.Count > 0)\r
208                         {\r
209                                 types = new Type [lookupTypes.Count];\r
210                                 for (int n=0; n<lookupTypes.Count; n++)\r
211                                 {\r
212                                         Type t = assembly.GetType ((string)lookupTypes[n]);\r
213                                         if (t == null) Error (typeNotFound, (string)lookupTypes[n]);\r
214                                         types[n] = t;\r
215                                 }\r
216                         }\r
217                         else\r
218                                 types = assembly.GetExportedTypes ();\r
219 \r
220                         XmlReflectionImporter ri = new XmlReflectionImporter ();\r
221                         XmlSchemas schemas = new XmlSchemas ();\r
222                         XmlSchemaExporter sx = new XmlSchemaExporter (schemas);\r
223 \r
224                         foreach (Type type in types)\r
225                         {\r
226                                 XmlTypeMapping tm = ri.ImportTypeMapping (type);\r
227                                 sx.ExportTypeMapping (tm);\r
228                         }\r
229 \r
230                         if (schemas.Count == 1)\r
231                         {\r
232                                 string fileName = Path.Combine (outputDir, "schema.xsd");\r
233                                 WriteSchema (fileName, schemas [0]);\r
234                         }\r
235                         else\r
236                         {\r
237                                 for (int n=0; n<schemas.Count; n++)\r
238                                 {\r
239                                         string fileName = Path.Combine (outputDir, "schema" + n + ".xsd");\r
240                                         WriteSchema (fileName, schemas [n]);\r
241                                 }\r
242                         }\r
243                 }\r
244 \r
245                 void WriteSchema (string fileName, XmlSchema schema)\r
246                 {\r
247                         StreamWriter sw = new StreamWriter (fileName);\r
248                         schema.Write (sw);\r
249                         sw.Close ();\r
250                         Console.WriteLine ("Written file " + fileName);\r
251                 }\r
252                 \r
253                 public void GenerateClasses ()\r
254                 {\r
255                         if (language != null && language != "CS") Error (languageNotSupported, language);\r
256                         if (namesp == null) namesp = "Schemas";\r
257                         if (uri == null) uri = "";\r
258                         string targetFile = "";\r
259 \r
260                         XmlSchemas schemas = new XmlSchemas();\r
261                         foreach (string fileName in schemaNames)\r
262                         {\r
263                                 StreamReader sr = new StreamReader (fileName);\r
264                                 schemas.Add (XmlSchema.Read (sr, null));\r
265                                 sr.Close ();\r
266 \r
267                                 if (targetFile == "") targetFile = Path.GetFileNameWithoutExtension (fileName);\r
268                                 else targetFile += "_" + Path.GetFileNameWithoutExtension (fileName);\r
269                         }\r
270 \r
271                         targetFile += ".cs";\r
272 \r
273                         CodeCompileUnit cunit = new CodeCompileUnit ();\r
274                         CodeNamespace codeNamespace = new CodeNamespace (namesp);\r
275                         cunit.Namespaces.Add (codeNamespace);\r
276                         codeNamespace.Comments.Add (new CodeCommentStatement ("\nThis source code was auto-generated by MonoXSD\n"));\r
277 \r
278                         // Locate elements to generate\r
279 \r
280                         ArrayList qnames = new ArrayList ();\r
281                         if (elements.Count > 0)\r
282                         {\r
283                                 foreach (string name in elements)\r
284                                         qnames.Add (new XmlQualifiedName (name, uri));\r
285                         }\r
286                         else\r
287                         {\r
288                                 foreach (XmlSchema schema in schemas) {\r
289                                         if (!schema.IsCompiled) schema.Compile (null);\r
290                                         foreach (XmlSchemaElement elem in schema.Elements.Values)\r
291                                                 qnames.Add (elem.QualifiedName);\r
292                                 }\r
293                         }\r
294 \r
295                         // Import schemas and generate the class model\r
296 \r
297                         XmlSchemaImporter importer = new XmlSchemaImporter (schemas);\r
298                         XmlCodeExporter sx = new XmlCodeExporter (codeNamespace, cunit);\r
299 \r
300                         ArrayList maps = new ArrayList();\r
301 \r
302                         foreach (XmlQualifiedName qname in qnames)\r
303                         {\r
304                                 XmlTypeMapping tm = importer.ImportTypeMapping (qname);\r
305                                 if (tm != null) maps.Add (tm);\r
306                         }\r
307                         \r
308                         foreach (XmlTypeMapping tm in maps)\r
309                         {\r
310                                 sx.ExportTypeMapping (tm);\r
311                         }\r
312 \r
313                         // Generate the code\r
314                         \r
315                         CSharpCodeProvider provider = new CSharpCodeProvider();\r
316                         ICodeGenerator gen = provider.CreateGenerator();\r
317 \r
318                         string genFile = Path.Combine (outputDir, targetFile);\r
319                         StreamWriter sw = new StreamWriter(genFile, false);\r
320                         gen.GenerateCodeFromCompileUnit (cunit, sw, new CodeGeneratorOptions());\r
321                         sw.Close();\r
322 \r
323                         Console.WriteLine ("Written file " + genFile);\r
324                 }\r
325 \r
326                 public void Error (string msg)\r
327                 {\r
328                         throw new Exception (msg);\r
329                 }\r
330 \r
331                 public void Error (string msg, string param)\r
332                 {\r
333                         throw new Exception (string.Format(msg,param));\r
334                 }\r
335         }\r
336 }\r