merge -r 60814:60815
[mono.git] / mcs / tools / wsdl / MonoWSDL.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@ximian.com)\r
6 ///\r
7 /// Copyright (C) 2003, Erik LeBel,\r
8 ///\r
9 \r
10 #if !NET_2_0\r
11 \r
12 using System;\r
13 using System.Xml;\r
14 using System.Xml.Schema;\r
15 using System.Collections;\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.Reflection;\r
23 \r
24 using Microsoft.CSharp;\r
25 \r
26 namespace Mono.WebServices\r
27 {\r
28         ///\r
29         /// <summary>\r
30         ///     Source code generator.\r
31         /// </summary>\r
32         ///\r
33         class SourceGenerator\r
34         {\r
35                 string applicationSiganture     = null;\r
36                 string appSettingURLKey         = null;\r
37                 string appSettingBaseURL        = null;\r
38                 string language                 = "CS";\r
39                 string ns                       = null;\r
40                 string outFilename              = null;\r
41                 string protocol                 = "Soap";\r
42                 bool   server                   = false;\r
43                 \r
44                 ///\r
45                 /// <summary/>\r
46                 ///\r
47                 public string Language \r
48                 {\r
49                         // FIXME validate\r
50                         set { language = value; }\r
51                 }\r
52                 \r
53                 ///\r
54                 /// <summary/>\r
55                 ///\r
56                 public string Namespace \r
57                 {\r
58                         set { ns = value; }\r
59                 }\r
60                 \r
61                 ///\r
62                 /// <summary>\r
63                 ///     The file to contain the generated code.\r
64                 /// </summary>\r
65                 ///\r
66                 public string Filename\r
67                 {\r
68                         set { outFilename = value; }\r
69                 }\r
70                 \r
71                 ///\r
72                 /// <summary/>\r
73                 ///\r
74                 public string Protocol\r
75                 {\r
76                         // FIXME validate\r
77                         set { protocol = value; }\r
78                         get { return protocol; }\r
79                 }\r
80                 \r
81                 ///\r
82                 /// <summary/>\r
83                 ///\r
84                 public string ApplicationSignature\r
85                 {\r
86                         set { applicationSiganture = value; }\r
87                 }\r
88                 \r
89                 ///\r
90                 /// <summary/>\r
91                 ///\r
92                 public string AppSettingURLKey\r
93                 {\r
94                         set { appSettingURLKey = value; }\r
95                 }\r
96                 \r
97                 ///\r
98                 /// <summary/>\r
99                 ///\r
100                 public string AppSettingBaseURL \r
101                 {\r
102                         set { appSettingBaseURL  = value; }\r
103                 }\r
104                 \r
105                 ///\r
106                 /// <summary/>\r
107                 ///\r
108                 public bool Server\r
109                 {\r
110                         set { server = value; }\r
111                 }\r
112                 \r
113                 ///\r
114                 /// <summary>\r
115                 ///     Generate code for the specified ServiceDescription.\r
116                 /// </summary>\r
117                 ///\r
118                 public bool GenerateCode (ArrayList descriptions, ArrayList schemas)\r
119                 {\r
120                         // FIXME iterate over each serviceDescription.Services?\r
121                         CodeNamespace codeNamespace = GetCodeNamespace();\r
122                         CodeCompileUnit codeUnit = new CodeCompileUnit();\r
123                         bool hasWarnings = false;\r
124                         \r
125                         codeUnit.Namespaces.Add(codeNamespace);\r
126 \r
127                         ServiceDescriptionImporter importer = new ServiceDescriptionImporter();\r
128                         importer.ProtocolName = protocol;\r
129                         if (server)\r
130                                 importer.Style = ServiceDescriptionImportStyle.Server;\r
131                         \r
132                         foreach (ServiceDescription sd in descriptions)\r
133                                 importer.AddServiceDescription(sd, appSettingURLKey, appSettingBaseURL);\r
134                                 \r
135                         foreach (XmlSchema sc in schemas)\r
136                                 importer.Schemas.Add (sc);\r
137                         \r
138                         ServiceDescriptionImportWarnings warnings = importer.Import(codeNamespace, codeUnit);\r
139                         if (warnings != 0)\r
140                         {\r
141                                 if ((warnings & ServiceDescriptionImportWarnings.NoCodeGenerated) > 0)\r
142                                         Console.WriteLine ("WARNING: No proxy class was generated"); \r
143                                 if ((warnings & ServiceDescriptionImportWarnings.NoMethodsGenerated) > 0)\r
144                                         Console.WriteLine ("WARNING: The proxy class generated includes no methods");\r
145                                 if ((warnings & ServiceDescriptionImportWarnings.OptionalExtensionsIgnored) > 0)\r
146                                         Console.WriteLine ("WARNING: At least one optional extension has been ignored");\r
147                                 if ((warnings & ServiceDescriptionImportWarnings.RequiredExtensionsIgnored) > 0)\r
148                                         Console.WriteLine ("WARNING: At least one necessary extension has been ignored");\r
149                                 if ((warnings & ServiceDescriptionImportWarnings.UnsupportedBindingsIgnored) > 0)\r
150                                         Console.WriteLine ("WARNING: At least one binding is of an unsupported type and has been ignored");\r
151                                 if ((warnings & ServiceDescriptionImportWarnings.UnsupportedOperationsIgnored) > 0)\r
152                                         Console.WriteLine ("WARNING: At least one operation is of an unsupported type and has been ignored");\r
153                                 hasWarnings = true;\r
154                         }\r
155                         \r
156                         string fileName = null;\r
157                         bool hasBindings = false;\r
158                         \r
159                         foreach (ServiceDescription desc in descriptions)\r
160                         {\r
161                                 if (fileName == null && desc.Services.Count > 0)\r
162                                         fileName = desc.Services[0].Name;\r
163 \r
164                                 if (desc.Bindings.Count > 0 || desc.Services.Count > 0)\r
165                                         hasBindings = true;\r
166                         }\r
167                         \r
168                         if (fileName == null)\r
169                                 fileName = "output";\r
170                         \r
171                         if (hasBindings)\r
172                                 WriteCodeUnit (codeUnit, fileName);\r
173                         \r
174                         return hasWarnings;\r
175                 }\r
176                 \r
177                 ///\r
178                 /// <summary>\r
179                 ///     Create the CodeNamespace with the generator's signature commented in.\r
180                 /// </summary>\r
181                 ///\r
182                 CodeNamespace GetCodeNamespace()\r
183                 {\r
184                         CodeNamespace codeNamespace = new CodeNamespace(ns);\r
185                         \r
186                         if (applicationSiganture != null)\r
187                         {\r
188                                 codeNamespace.Comments.Add(new CodeCommentStatement("\n This source code was auto-generated by " + applicationSiganture + "\n"));\r
189                         }\r
190                         \r
191                         return codeNamespace;\r
192                 }\r
193                 \r
194                 ///\r
195                 /// <summary/>\r
196                 ///\r
197                 void WriteCodeUnit(CodeCompileUnit codeUnit, string serviceName)\r
198                 {\r
199                         CodeDomProvider provider = GetProvider();\r
200                         ICodeGenerator generator = provider.CreateGenerator();\r
201                         CodeGeneratorOptions options = new CodeGeneratorOptions();\r
202                         \r
203                         string filename;\r
204                         if (outFilename != null)\r
205                                 filename = outFilename;\r
206                         else\r
207                                 filename = serviceName  + "." + provider.FileExtension;\r
208                         \r
209                         Console.WriteLine ("Writing file '{0}'", filename);\r
210                         StreamWriter writer = new StreamWriter(filename);\r
211                         generator.GenerateCodeFromCompileUnit(codeUnit, writer, options);\r
212                         writer.Close();\r
213                 }\r
214                 \r
215                 ///\r
216                 /// <summary>\r
217                 ///     Fetch the Code Provider for the language specified by the 'language' members.\r
218                 /// </summary>\r
219                 ///\r
220                 private CodeDomProvider GetProvider()\r
221                 {\r
222                         CodeDomProvider provider;\r
223                         Type type;\r
224                         \r
225                         switch (language.ToUpper ()) {\r
226                         case "CS":\r
227                                 provider = new CSharpCodeProvider ();\r
228                                 break;\r
229                         case "VB":\r
230                                 provider = new Microsoft.VisualBasic.VBCodeProvider ();\r
231                                 break;\r
232                         case "BOO":\r
233                                 type = Type.GetType("Boo.Lang.CodeDom.BooCodeProvider, Boo.Lang.CodeDom, Version=1.0.0.0, Culture=neutral, PublicKeyToken=32c39770e9a21a67");\r
234                                 if (type != null){\r
235                                         return (CodeDomProvider) Activator.CreateInstance (type);\r
236                                 }\r
237                                 throw new Exception ("Boo.Lang.CodeDom.BooCodeProvider not available");\r
238                                                          \r
239                         default:\r
240                                 type = Type.GetType(language);\r
241                                 if (type != null) {\r
242                                         return (CodeDomProvider) Activator.CreateInstance (type);\r
243                                 }       \r
244                                 throw new Exception ("Unknown language");\r
245                         }\r
246                         return provider;\r
247                 }\r
248         }\r
249         \r
250         ///\r
251         /// <summary>\r
252         ///     monoWSDL's main application driver. Reads the command-line arguments and dispatch the\r
253         ///     appropriate handlers.\r
254         /// </summary>\r
255         ///\r
256         public class Driver\r
257         {\r
258                 const string ProductId = "Mono Web Services Description Language Utility";\r
259                 const string UsageMessage = \r
260                         "wsdl [options] {path | URL} \n\n"\r
261                         + "   -d, -domain:domain           Domain of username for server authentication.\n"\r
262                         + "   -l, -language:language       Language of generated code. Allowed CS (default)\n"\r
263                         + "                                and VB. You can also specify the fully qualified\n"\r
264                         + "                                name of a class that implements the\n"\r
265                         + "                                System.CodeDom.Compiler.CodeDomProvider Class.\n"\r
266                         + "   -n, -namespace:ns            The namespace of the generated code, default\n"\r
267                         + "                                namespace if none.\n"\r
268                         + "   -nologo                      Surpress the startup logo.\n"\r
269                         + "   -o, -out:filename            The target file for generated code.\n"\r
270                         + "   -p, -password:pwd            Password used to contact the server.\n"\r
271                         + "   -protocol:protocol           Protocol to implement. Allowed: Soap (default),\n"\r
272                         + "                                HttpGet or HttpPost.\n"\r
273                         + "   -server                      Generate server instead of client proxy code.\n"\r
274                         + "   -u, -username:username       Username used to contact the server.\n"\r
275                         + "   -proxy:url                   Address of the proxy.\n"\r
276                         + "   -pu, -proxyusername:username Username used to contact the proxy.\n"\r
277                         + "   -pp, -proxypassword:pwd      Password used to contact the proxy.\n"\r
278                         + "   -pd, -proxydomain:domain     Domain of username for proxy authentication.\n"\r
279                         + "   -urlkey, -appsettingurlkey:key Configuration key that contains the default\n"\r
280                         + "                                url for the generated WS proxy.\n"\r
281                         + "   -baseurl, -appsettingbaseurl:url Base url to use when constructing the\n"\r
282                         + "                                service url.\n"\r
283                         + "   -type:typename,assembly      Generate a proxy for a compiled web service\n"\r
284                         + "                                class. The URL parameter can be used to provide\n"\r
285                         + "                                the location of the service.\n"\r
286                         + "   -sample:[binding/]operation  Display a sample SOAP request and response.\n"\r
287                         + "   -?                           Display this message\n"\r
288                         + "\n"\r
289                         + "Options can be of the forms  -option, --option or /option\n";\r
290                 \r
291                 SourceGenerator generator = null;\r
292                 \r
293                 ArrayList descriptions = new ArrayList ();\r
294                 ArrayList schemas = new ArrayList ();\r
295                 \r
296                 bool noLogo = false;\r
297                 bool help = false;\r
298                 bool hasURL = false;\r
299                 string sampleSoap = null;\r
300                 \r
301                 string proxyAddress = null;\r
302                 string proxyDomain = null;\r
303                 string proxyPassword = null;\r
304                 string proxyUsername = null;\r
305                 string username;\r
306                 string password;\r
307                 string domain;\r
308                 \r
309                 string url;\r
310                 string className;\r
311 \r
312                 ///\r
313                 /// <summary>\r
314                 ///     Initialize the document retrieval component and the source code generator.\r
315                 /// </summary>\r
316                 ///\r
317                 Driver()\r
318                 {\r
319                         generator = new SourceGenerator();\r
320                         generator.ApplicationSignature = ProductId;\r
321                 }\r
322                 \r
323                 ///\r
324                 /// <summary>\r
325                 ///     Interperet the command-line arguments and configure the relavent components.\r
326                 /// </summary>\r
327                 ///             \r
328                 void ImportArgument(string argument)\r
329                 {\r
330                         string optionValuePair;\r
331                         \r
332                         if (argument.StartsWith("--"))\r
333                         {\r
334                                 optionValuePair = argument.Substring(2);\r
335                         }\r
336                         else if (argument.StartsWith("/") || argument.StartsWith("-"))\r
337                         {\r
338                                 optionValuePair = argument.Substring(1);\r
339                         }\r
340                         else\r
341                         {\r
342                                 hasURL = true;\r
343                                 url = argument;\r
344                                 return;\r
345                         }\r
346                         \r
347                         string option;\r
348                         string value;\r
349                         \r
350                         int indexOfEquals = optionValuePair.IndexOf(':');\r
351                         if (indexOfEquals > 0)\r
352                         {\r
353                                 option = optionValuePair.Substring(0, indexOfEquals);\r
354                                 value = optionValuePair.Substring(indexOfEquals + 1);\r
355                         }\r
356                         else\r
357                         {\r
358                                 option = optionValuePair;\r
359                                 value = null;\r
360                         }\r
361                         \r
362                         switch (option)\r
363                         {\r
364                                 case "appsettingurlkey":\r
365                                 case "urlkey":\r
366                                     generator.AppSettingURLKey = value;\r
367                                     break;\r
368 \r
369                                 case "appsettingbaseurl":\r
370                                 case "baseurl":\r
371                                     generator.AppSettingBaseURL = value;\r
372                                     break;\r
373 \r
374                                 case "d":\r
375                                 case "domain":\r
376                                     domain = value;\r
377                                     break;\r
378 \r
379                                 case "l":\r
380                                 case "language":\r
381                                     generator.Language = value;\r
382                                     break;\r
383 \r
384                                 case "n":\r
385                                 case "namespace":\r
386                                     generator.Namespace = value;\r
387                                     break;\r
388 \r
389                                 case "nologo":\r
390                                     noLogo = true;\r
391                                     break;\r
392 \r
393                                 case "o":\r
394                                 case "out":\r
395                                     generator.Filename = value;\r
396                                     break;\r
397 \r
398                                 case "p":\r
399                                 case "password":\r
400                                     password = value;\r
401                                     break;\r
402 \r
403                                 case "protocol":\r
404                                     generator.Protocol = value;\r
405                                     break;\r
406 \r
407                                 case "proxy":\r
408                                     proxyAddress = value;\r
409                                     break;\r
410 \r
411                                 case "proxydomain":\r
412                                 case "pd":\r
413                                     proxyDomain = value;\r
414                                     break;\r
415 \r
416                                 case "proxypassword":\r
417                                 case "pp":\r
418                                     proxyPassword = value;\r
419                                     break;\r
420 \r
421                                 case "proxyusername":\r
422                                 case "pu":\r
423                                     proxyUsername = value;\r
424                                     break;\r
425 \r
426                                 case "server":\r
427                                     generator.Server = true;\r
428                                     break;\r
429 \r
430                                 case "u":\r
431                                 case "username":\r
432                                     username = value;\r
433                                     break;\r
434                                         \r
435                                 case "sample":\r
436                                         sampleSoap = value;\r
437                                         break;\r
438                                         \r
439                                 case "type":\r
440                                 case "t":\r
441                                         className = value;\r
442                                         break;\r
443                                         \r
444                                 case "?":\r
445                                     help = true;\r
446                                     break;\r
447 \r
448                                 default:\r
449                                         if (argument.StartsWith ("/") && argument.IndexOfAny (Path.InvalidPathChars) == -1) {\r
450                                                 hasURL = true;\r
451                                                 url = argument;\r
452                                                 break;\r
453                                         }\r
454                                         else\r
455                                             throw new Exception("Unknown option '" + option + "'");\r
456                         }\r
457                 }\r
458                 \r
459                 DiscoveryClientProtocol CreateClient ()\r
460                 {\r
461                         DiscoveryClientProtocol dcc = new DiscoveryClientProtocol ();\r
462                         \r
463                         if (username != null || password != null || domain != null)\r
464                         {\r
465                                 NetworkCredential credentials = new NetworkCredential();\r
466                                 \r
467                                 if (username != null)\r
468                                         credentials.UserName = username;\r
469                                 \r
470                                 if (password != null)\r
471                                         credentials.Password = password;\r
472                                 \r
473                                 if (domain != null)\r
474                                         credentials.Domain = domain;\r
475                                 \r
476                                 dcc.Credentials = credentials;\r
477                         }\r
478                         \r
479                         if (proxyAddress != null)\r
480                         {\r
481                                 WebProxy proxy = new WebProxy (proxyAddress);\r
482                                 if (proxyUsername != null || proxyPassword != null || proxyDomain != null)\r
483                                 {\r
484                                         NetworkCredential credentials = new NetworkCredential();\r
485                                         \r
486                                         if (proxyUsername != null)\r
487                                                 credentials.UserName = proxyUsername;\r
488                                         \r
489                                         if (proxyPassword != null)\r
490                                                 credentials.Password = proxyPassword;\r
491                                         \r
492                                         if (proxyDomain != null)\r
493                                                 credentials.Domain = proxyDomain;\r
494                                         \r
495                                         proxy.Credentials = credentials;\r
496                                 }\r
497                         }                       \r
498                         \r
499                         return dcc;\r
500                 }\r
501 \r
502                 ///\r
503                 /// <summary>\r
504                 ///     Driver's main control flow:\r
505                 ///      - parse arguments\r
506                 ///      - report required messages\r
507                 ///      - terminate if no input\r
508                 ///      - report errors\r
509                 /// </summary>\r
510                 ///\r
511                 int Run(string[] args)\r
512                 {\r
513                         try\r
514                         {\r
515                                 // parse command line arguments\r
516                                 foreach (string argument in args)\r
517                                 {\r
518                                         ImportArgument(argument);\r
519                                 }\r
520                                 \r
521                                 if (noLogo == false)\r
522                                         Console.WriteLine(ProductId);\r
523                                 \r
524                                 if (help || (!hasURL && className == null))\r
525                                 {\r
526                                         Console.WriteLine(UsageMessage);\r
527                                         return 0;\r
528                                 }\r
529                                 \r
530                                 if (className == null)\r
531                                 {\r
532                                         DiscoveryClientProtocol dcc = CreateClient ();\r
533                                         dcc.AllowAutoRedirect = true;\r
534                                                                         \r
535                                         if (!url.StartsWith ("http://") && !url.StartsWith ("https://") && !url.StartsWith ("file://"))\r
536                                                 url = new Uri (Path.GetFullPath (url)).ToString ();\r
537                                                 \r
538                                         dcc.DiscoverAny (url);\r
539                                         dcc.ResolveAll ();\r
540                                         \r
541                                         foreach (object doc in dcc.Documents.Values)\r
542                                         {\r
543                                                 if (doc is ServiceDescription)\r
544                                                         descriptions.Add ((ServiceDescription)doc);\r
545                                                 else if (doc is XmlSchema)\r
546                                                         schemas.Add ((XmlSchema)doc);\r
547                                         }\r
548                                         \r
549                                         if (descriptions.Count == 0)\r
550                                                 throw new Exception ("No WSDL document was found at the url " + url);\r
551                                 }\r
552                                 else\r
553                                 {\r
554                                         string[] names = className.Split (',');\r
555                                         if (names.Length != 2) throw new Exception ("Invalid parameter value for 'type'");\r
556                                         string cls = names[0].Trim ();\r
557                                         string assembly = names[1].Trim ();\r
558                                         \r
559                                         Assembly asm = Assembly.LoadFrom (assembly);\r
560                                         Type t = asm.GetType (cls);\r
561                                         if (t == null) throw new Exception ("Type '" + cls + "' not found in assembly " + assembly);\r
562                                         ServiceDescriptionReflector reflector = new ServiceDescriptionReflector ();\r
563                                         reflector.Reflect (t, url);\r
564                                         foreach (XmlSchema s in reflector.Schemas)\r
565                                                 schemas.Add (s);\r
566                                                 \r
567                                         foreach (ServiceDescription sd in reflector.ServiceDescriptions)\r
568                                                 descriptions.Add (sd);\r
569                                 }\r
570                                 \r
571                                 if (sampleSoap != null)\r
572                                 {\r
573                                         ConsoleSampleGenerator.Generate (descriptions, schemas, sampleSoap, generator.Protocol);\r
574                                         return 0;\r
575                                 }\r
576                                 \r
577                                 // generate the code\r
578                                 if (generator.GenerateCode (descriptions, schemas))\r
579                                         return 1;\r
580                                 else\r
581                                         return 0;\r
582                         }\r
583                         catch (NullReferenceException e)\r
584                         {\r
585                                 Console.WriteLine (e);\r
586                                 return 2;\r
587                         }\r
588                         catch (Exception exception)\r
589                         {\r
590                                 Console.WriteLine("Error: {0}", exception.Message);\r
591                                 // FIXME: surpress this except for when debug is enabled\r
592                                 //Console.WriteLine("Stack:\n {0}", exception.StackTrace);\r
593                                 return 2;\r
594                         }\r
595                 }\r
596                                 \r
597                 ///\r
598                 /// <summary>\r
599                 ///     Application entry point.\r
600                 /// </summary>\r
601                 ///\r
602                 public static int Main(string[] args)\r
603                 {\r
604                         Driver d = new Driver();\r
605                         return d.Run(args);\r
606                 }\r
607         }\r
608 }\r
609 \r
610 #endif\r