2004-08-27 Martin Baulig <martin@ximian.com>
[mono.git] / mcs / tools / wsdl / MonoWSDL2.cs
1 ///\r
2 /// MonoWSDL.cs -- a WSDL to proxy code generator.\r
3 ///\r
4 /// Author: Erik LeBel (eriklebel@yahoo.ca)\r
5 ///             Lluis Sanchez (lluis@novell.com)\r
6 ///\r
7 /// Copyright (C) 2003, Erik LeBel,\r
8 ///\r
9 \r
10 using System;\r
11 using System.Xml;\r
12 using System.Xml.Serialization;\r
13 using System.Xml.Schema;\r
14 using System.Collections;\r
15 using System.Collections.Specialized;\r
16 using System.CodeDom;\r
17 using System.CodeDom.Compiler;\r
18 using System.IO;\r
19 using System.Net;\r
20 using System.Web.Services.Description;\r
21 using System.Web.Services.Discovery;\r
22 using System.Web.Services;\r
23 \r
24 using Microsoft.CSharp;\r
25 \r
26 namespace Mono.WebServices\r
27 {\r
28         public class Driver\r
29         {\r
30                 string ProductId = "Web Services Description Language Utility\nMono Framework v" + Environment.Version;\r
31                 const string UsageMessage = \r
32                         "wsdl [options] {path | URL} {path | URL} ...\n\n"\r
33                         + "   -d, -domain:domain           Domain of username for server authentication.\n"\r
34                         + "   -l, -language:language       Language of generated code. Allowed CS (default)\n"\r
35                         + "                                and VB.\n"\r
36                         + "   -n, -namespace:ns            The namespace of the generated code, default\n"\r
37                         + "                                namespace if none.\n"\r
38                         + "   -nologo                      Surpress the startup logo.\n"\r
39                         + "   -o, -out:filename            The target file for generated code.\n"\r
40                         + "   -p, -password:pwd            Password used to contact the server.\n"\r
41                         + "   -protocol:protocol           Protocol to implement. Allowed: Soap (default),\n"\r
42                         + "                                HttpGet or HttpPost.\n"\r
43                         + "   -fields                      Generate fields instead of properties in data\n"\r
44                         + "                                classes.\n"\r
45                         + "   -server                      Generate server instead of client proxy code.\n"\r
46                         + "   -u, -username:username       Username used to contact the server.\n"\r
47                         + "   -proxy:url                   Address of the proxy.\n"\r
48                         + "   -pu, -proxyusername:username Username used to contact the proxy.\n"\r
49                         + "   -pp, -proxypassword:pwd      Password used to contact the proxy.\n"\r
50                         + "   -pd, -proxydomain:domain     Domain of username for proxy authentication.\n"\r
51                         + "   -urlkey, -appsettingurlkey:key Configuration key that contains the default\n"\r
52                         + "                                url for the generated WS proxy.\n"\r
53                         + "   -baseurl, -appsettingbaseurl:url Base url to use when constructing the\n"\r
54                         + "                                service url.\n"\r
55                         + "   -sample:[binding/]operation  Display a sample SOAP request and response.\n"\r
56                         + "   -?                           Display this message\n"\r
57                         + "\n"\r
58                         + "Options can be of the forms  -option, --option or /option\n";\r
59                 \r
60                 ArrayList descriptions = new ArrayList ();\r
61                 ArrayList schemas = new ArrayList ();\r
62                 \r
63                 bool noLogo;\r
64                 bool help;\r
65                 string sampleSoap;\r
66                 \r
67                 string proxyAddress;\r
68                 string proxyDomain;\r
69                 string proxyPassword;\r
70                 string proxyUsername;\r
71                 string username;\r
72                 string password;\r
73                 string domain;\r
74                 \r
75                 string applicationSignature;\r
76                 string appSettingURLKey;\r
77                 string appSettingBaseURL;\r
78                 string language = "CS";\r
79                 string ns;\r
80                 string outFilename;\r
81                 string protocol = "Soap";\r
82                 ServiceDescriptionImportStyle style;\r
83                 CodeGenerationOptions options = CodeGenerationOptions.GenerateProperties | CodeGenerationOptions.GenerateNewAsync;\r
84                 bool verbose;\r
85                 \r
86                 StringCollection urls = new StringCollection ();\r
87 \r
88                 ///\r
89                 /// <summary>\r
90                 ///     Application entry point.\r
91                 /// </summary>\r
92                 ///\r
93                 public static int Main(string[] args)\r
94                 {\r
95                         Driver d = new Driver();\r
96                         return d.Run(args);\r
97                 }\r
98                 \r
99                 Driver()\r
100                 {\r
101                         applicationSignature = ProductId;\r
102                 }\r
103                 \r
104                 int Run (string[] args)\r
105                 {\r
106                         try\r
107                         {\r
108                                 // parse command line arguments\r
109                                 foreach (string argument in args)\r
110                                         ImportArgument(argument);\r
111                                 \r
112                                 if (noLogo == false)\r
113                                         Console.WriteLine(ProductId);\r
114                                 \r
115                                 if (help || urls.Count == 0)\r
116                                 {\r
117                                         Console.WriteLine(UsageMessage);\r
118                                         return 0;\r
119                                 }\r
120                                 \r
121                                 CodeCompileUnit codeUnit = new CodeCompileUnit();\r
122                                 CodeNamespace proxyCode = GetCodeNamespace();\r
123                                 codeUnit.Namespaces.Add (proxyCode);\r
124                                 \r
125                                 WebReferenceCollection references = new WebReferenceCollection ();\r
126                                 foreach (string url in urls) \r
127                                 {\r
128                                         DiscoveryClientProtocol dcc = CreateClient ();\r
129                                                                         \r
130                                         dcc.DiscoverAny (url);\r
131                                         dcc.ResolveAll ();\r
132                                         \r
133                                         WebReference reference = new WebReference (dcc.Documents, proxyCode, protocol, appSettingURLKey, appSettingBaseURL);\r
134                                         references.Add (reference);\r
135                                         \r
136                                         if (sampleSoap != null)\r
137                                                 ConsoleSampleGenerator.Generate (descriptions, schemas, sampleSoap, protocol);\r
138                                 }\r
139                                 \r
140                                 if (sampleSoap != null)\r
141                                         return 0;\r
142                                         \r
143                                 // generate the code\r
144                                 if (GenerateCode (references, codeUnit))\r
145                                         return 1;\r
146                                 else\r
147                                         return 0;\r
148                         }\r
149                         catch (Exception exception)\r
150                         {\r
151                                 Console.WriteLine("Error: {0}", exception.Message);\r
152                                 \r
153                                 // Supress this except for when debug is enabled\r
154                                 Console.WriteLine("Stack:\n {0}", exception.StackTrace);\r
155                                 return 2;\r
156                         }\r
157                 }\r
158                 \r
159                 ///\r
160                 /// <summary>\r
161                 ///     Generate code for the specified ServiceDescription.\r
162                 /// </summary>\r
163                 ///\r
164                 public bool GenerateCode (WebReferenceCollection references, CodeCompileUnit codeUnit)\r
165                 {\r
166                         bool hasWarnings = false;\r
167                         \r
168                         CodeDomProvider provider = GetProvider();\r
169                         ICodeGenerator generator = provider.CreateGenerator();\r
170                                 \r
171                         StringCollection validationWarnings;\r
172                         validationWarnings = ServiceDescriptionImporter.GenerateWebReferences (references, options, style, generator, codeUnit, verbose);\r
173                         \r
174                         for (int n=0; n<references.Count; n++)\r
175                         {\r
176                                 WebReference wr  = references [n];\r
177                                 \r
178                                 BasicProfileViolationCollection violations = new BasicProfileViolationCollection ();\r
179                                 if (!WebServicesInteroperability.CheckConformance (WsiClaims.BP10, wr, violations)) {\r
180                                         wr.Warnings |= ServiceDescriptionImportWarnings.WsiConformance;\r
181                                 }\r
182                                 \r
183                                 if (wr.Warnings != 0)\r
184                                 {\r
185                                         if (!hasWarnings) {\r
186                                                 WriteText ("", 0, 0);\r
187                                                 WriteText ("There where some warnings while generating the code:", 0, 0);\r
188                                         }\r
189                                         \r
190                                         WriteText ("", 0, 0);\r
191                                         WriteText (urls[n], 2, 2);\r
192                                         \r
193                                         if ((wr.Warnings & ServiceDescriptionImportWarnings.WsiConformance) > 0) {\r
194                                                 WriteText ("- This web reference does not conform to WS-I Basic Profile v1.0", 4, 6); \r
195                                                 foreach (BasicProfileViolation vio in violations) {\r
196                                                         WriteText (vio.NormativeStatement + ": " + vio.Details, 8, 8);\r
197                                                         foreach (string ele in vio.Elements)\r
198                                                                 WriteText ("* " + ele, 10, 12);\r
199                                                 }\r
200                                         }\r
201                                         \r
202                                         if ((wr.Warnings & ServiceDescriptionImportWarnings.NoCodeGenerated) > 0)\r
203                                                 WriteText ("- WARNING: No proxy class was generated", 4, 6); \r
204                                         if ((wr.Warnings & ServiceDescriptionImportWarnings.NoMethodsGenerated) > 0)\r
205                                                 WriteText ("- WARNING: The proxy class generated includes no methods", 4, 6);\r
206                                         if ((wr.Warnings & ServiceDescriptionImportWarnings.OptionalExtensionsIgnored) > 0)\r
207                                                 WriteText ("- WARNING: At least one optional extension has been ignored", 4, 6);\r
208                                         if ((wr.Warnings & ServiceDescriptionImportWarnings.RequiredExtensionsIgnored) > 0)\r
209                                                 WriteText ("- WARNING: At least one necessary extension has been ignored", 4, 6);\r
210                                         if ((wr.Warnings & ServiceDescriptionImportWarnings.UnsupportedBindingsIgnored) > 0)\r
211                                                 WriteText ("- WARNING: At least one binding is of an unsupported type and has been ignored", 4, 6);\r
212                                         if ((wr.Warnings & ServiceDescriptionImportWarnings.UnsupportedOperationsIgnored) > 0)\r
213                                                 WriteText ("- WARNING: At least one operation is of an unsupported type and has been ignored", 4, 6);\r
214                                                 \r
215                                         hasWarnings = true;\r
216                                 }\r
217                         }\r
218                         
219                         if (hasWarnings) WriteText ("",0,0);\r
220                                 \r
221                         ServiceDescription rootDesc = null;\r
222                         
223                         foreach (object doc in references[0].Documents.Values) {\r
224                                 ServiceDescription desc = doc as ServiceDescription;
225                                 if (desc != null && desc.Services.Count > 0) {
226                                         rootDesc = desc;
227                                         break;
228                                 }
229                         }
230                                 \r
231                         if (rootDesc != null)\r
232                         {\r
233                                 string filename;\r
234                                 if (outFilename != null)\r
235                                         filename = outFilename;\r
236                                 else\r
237                                         filename = rootDesc.Services[0].Name    + "." + provider.FileExtension;\r
238                                 \r
239                                 WriteText ("Writing file '" + filename + "'", 0, 0);\r
240                                 StreamWriter writer = new StreamWriter(filename);\r
241                                 \r
242                                 CodeGeneratorOptions compilerOptions = new CodeGeneratorOptions();\r
243                                 generator.GenerateCodeFromCompileUnit (codeUnit, writer, compilerOptions);\r
244                                 writer.Close();\r
245                         }\r
246                         \r
247                         return hasWarnings;\r
248                 }\r
249                 \r
250                 ///\r
251                 /// <summary>\r
252                 ///     Create the CodeNamespace with the generator's signature commented in.\r
253                 /// </summary>\r
254                 ///\r
255                 CodeNamespace GetCodeNamespace()\r
256                 {\r
257                         CodeNamespace codeNamespace = new CodeNamespace(ns);\r
258                         \r
259                         if (applicationSignature != null)\r
260                         {\r
261                                 codeNamespace.Comments.Add(new CodeCommentStatement("\n This source code was auto-generated by " + applicationSignature + "\n"));\r
262                         }\r
263                         \r
264                         return codeNamespace;\r
265                 }\r
266                 \r
267                 ///\r
268                 /// <summary/>\r
269                 ///\r
270                 void WriteCodeUnit(CodeCompileUnit codeUnit, string serviceName)\r
271                 {\r
272                         CodeDomProvider provider = GetProvider();\r
273                         ICodeGenerator generator = provider.CreateGenerator();\r
274                         CodeGeneratorOptions options = new CodeGeneratorOptions();\r
275                         \r
276                         string filename;\r
277                         if (outFilename != null)\r
278                                 filename = outFilename;\r
279                         else\r
280                                 filename = serviceName  + "." + provider.FileExtension;\r
281                         \r
282                         Console.WriteLine ("Writing file '{0}'", filename);\r
283                         StreamWriter writer = new StreamWriter(filename);\r
284                         generator.GenerateCodeFromCompileUnit(codeUnit, writer, options);\r
285                         writer.Close();\r
286                 }\r
287                 \r
288                 ///\r
289                 /// <summary>\r
290                 ///     Fetch the Code Provider for the language specified by the 'language' members.\r
291                 /// </summary>\r
292                 ///\r
293                 private CodeDomProvider GetProvider()\r
294                 {\r
295                         // FIXME these should be loaded dynamically using reflection\r
296                         CodeDomProvider provider;\r
297                         \r
298                         switch (language.ToUpper())\r
299                         {\r
300                             case "CS":\r
301                                     provider = new CSharpCodeProvider();\r
302                                     break;\r
303                             \r
304                                 case "VB":\r
305                                         provider = new Microsoft.VisualBasic.VBCodeProvider();\r
306                                         break;\r
307                                         \r
308                             default:\r
309                                     throw new Exception("Unknow language");\r
310                         }\r
311 \r
312                         return provider;\r
313                 }\r
314                 \r
315 \r
316 \r
317                 ///\r
318                 /// <summary>\r
319                 ///     Interperet the command-line arguments and configure the relavent components.\r
320                 /// </summary>\r
321                 ///             \r
322                 void ImportArgument(string argument)\r
323                 {\r
324                         string optionValuePair;\r
325                         \r
326                         if (argument.StartsWith("--"))\r
327                         {\r
328                                 optionValuePair = argument.Substring(2);\r
329                         }\r
330                         else if (argument.StartsWith("/") || argument.StartsWith("-"))\r
331                         {\r
332                                 optionValuePair = argument.Substring(1);\r
333                         }\r
334                         else\r
335                         {\r
336                                 urls.Add (argument);\r
337                                 return;\r
338                         }\r
339                         \r
340                         string option;\r
341                         string value;\r
342                         \r
343                         int indexOfEquals = optionValuePair.IndexOf(':');\r
344                         if (indexOfEquals > 0)\r
345                         {\r
346                                 option = optionValuePair.Substring(0, indexOfEquals);\r
347                                 value = optionValuePair.Substring(indexOfEquals + 1);\r
348                         }\r
349                         else\r
350                         {\r
351                                 option = optionValuePair;\r
352                                 value = null;\r
353                         }\r
354                         \r
355                         switch (option)\r
356                         {\r
357                                 case "appsettingurlkey":\r
358                                 case "urlkey":\r
359                                     appSettingURLKey = value;\r
360                                     break;\r
361 \r
362                                 case "appsettingbaseurl":\r
363                                 case "baseurl":\r
364                                     appSettingBaseURL = value;\r
365                                     break;\r
366 \r
367                                 case "d":\r
368                                 case "domain":\r
369                                     domain = value;\r
370                                     break;\r
371 \r
372                                 case "l":\r
373                                 case "language":\r
374                                     language = value;\r
375                                     break;\r
376 \r
377                                 case "n":\r
378                                 case "namespace":\r
379                                     ns = value;\r
380                                     break;\r
381 \r
382                                 case "nologo":\r
383                                     noLogo = true;\r
384                                     break;\r
385 \r
386                                 case "o":\r
387                                 case "out":\r
388                                     outFilename = value;\r
389                                     break;\r
390 \r
391                                 case "p":\r
392                                 case "password":\r
393                                     password = value;\r
394                                     break;\r
395 \r
396                                 case "protocol":\r
397                                     protocol = value;\r
398                                     break;\r
399 \r
400                                 case "proxy":\r
401                                     proxyAddress = value;\r
402                                     break;\r
403 \r
404                                 case "proxydomain":\r
405                                 case "pd":\r
406                                     proxyDomain = value;\r
407                                     break;\r
408 \r
409                                 case "proxypassword":\r
410                                 case "pp":\r
411                                     proxyPassword = value;\r
412                                     break;\r
413 \r
414                                 case "proxyusername":\r
415                                 case "pu":\r
416                                     proxyUsername = value;\r
417                                     break;\r
418 \r
419                                 case "server":\r
420                                     style = ServiceDescriptionImportStyle.Server;\r
421                                     break;\r
422 \r
423                                 case "u":\r
424                                 case "username":\r
425                                     username = value;\r
426                                     break;\r
427                                         \r
428                                 case "verbose":\r
429                                         verbose = true;\r
430                                         break;\r
431                                         \r
432                                 case "fields":\r
433                                         options &= ~CodeGenerationOptions.GenerateProperties;\r
434                                         break;\r
435                                         \r
436                                 case "sample":\r
437                                         sampleSoap = value;\r
438                                         break;\r
439 \r
440                                 case "?":\r
441                                     help = true;\r
442                                     break;\r
443 \r
444                                 default:\r
445                                         if (argument.StartsWith ("/") && argument.IndexOfAny (Path.InvalidPathChars) == -1) {\r
446                                                 urls.Add (argument);\r
447                                                 break;\r
448                                         }\r
449                                         else\r
450                                             throw new Exception("Unknown option " + option);\r
451                         }\r
452                 }\r
453                 \r
454                 DiscoveryClientProtocol CreateClient ()\r
455                 {\r
456                         DiscoveryClientProtocol dcc = new DiscoveryClientProtocol ();\r
457                         \r
458                         if (username != null || password != null || domain != null)\r
459                         {\r
460                                 NetworkCredential credentials = new NetworkCredential();\r
461                                 \r
462                                 if (username != null)\r
463                                         credentials.UserName = username;\r
464                                 \r
465                                 if (password != null)\r
466                                         credentials.Password = password;\r
467                                 \r
468                                 if (domain != null)\r
469                                         credentials.Domain = domain;\r
470                                 \r
471                                 dcc.Credentials = credentials;\r
472                         }\r
473                         \r
474                         if (proxyAddress != null)\r
475                         {\r
476                                 WebProxy proxy = new WebProxy (proxyAddress);\r
477                                 if (proxyUsername != null || proxyPassword != null || proxyDomain != null)\r
478                                 {\r
479                                         NetworkCredential credentials = new NetworkCredential();\r
480                                         \r
481                                         if (proxyUsername != null)\r
482                                                 credentials.UserName = proxyUsername;\r
483                                         \r
484                                         if (proxyPassword != null)\r
485                                                 credentials.Password = proxyPassword;\r
486                                         \r
487                                         if (proxyDomain != null)\r
488                                                 credentials.Domain = proxyDomain;\r
489                                         \r
490                                         proxy.Credentials = credentials;\r
491                                 }\r
492                         }                       \r
493                         \r
494                         return dcc;\r
495                 }\r
496                 \r
497                 static void WriteText (string text, int initialLeftMargin, int leftMargin)\r
498                 {\r
499                         int n = 0;\r
500                         int margin = initialLeftMargin;\r
501                         int maxCols = 80;\r
502                         \r
503                         if (text == "") {\r
504                                 Console.WriteLine ();\r
505                                 return;\r
506                         }\r
507                         \r
508                         while (n < text.Length)\r
509                         {\r
510                                 int col = margin;\r
511                                 int lastWhite = -1;\r
512                                 int sn = n;\r
513                                 while (col < maxCols && n < text.Length) {\r
514                                         if (char.IsWhiteSpace (text[n]))\r
515                                                 lastWhite = n;\r
516                                         col++;\r
517                                         n++;\r
518                                 }\r
519                                 \r
520                                 if (lastWhite == -1 || col < maxCols)\r
521                                         lastWhite = n;\r
522                                 else if (col >= maxCols)\r
523                                         n = lastWhite + 1;\r
524                                 \r
525                                 Console.WriteLine (new String (' ', margin) + text.Substring (sn, lastWhite - sn));\r
526                                 margin = leftMargin;\r
527                         }\r
528                 }\r
529         }\r
530 }