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