Add MIT license to System.Web.Services.DLL
[mono.git] / mcs / class / System.Web.Services / System.Web.Services.Description / ProtocolImporter.cs
1 // 
2 // System.Web.Services.Description.ProtocolImporter.cs
3 //
4 // Author:
5 //   Tim Coleman (tim@timcoleman.com)
6 //   Lluis Sanchez Gual (lluis@ximian.com)
7 //
8 // Copyright (C) Tim Coleman, 2002
9 //
10
11 //
12 // Permission is hereby granted, free of charge, to any person obtaining
13 // a copy of this software and associated documentation files (the
14 // "Software"), to deal in the Software without restriction, including
15 // without limitation the rights to use, copy, modify, merge, publish,
16 // distribute, sublicense, and/or sell copies of the Software, and to
17 // permit persons to whom the Software is furnished to do so, subject to
18 // the following conditions:
19 // 
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
22 // 
23 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
24 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
25 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
26 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
27 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
28 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
29 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 //
31
32 using System;
33 using System.CodeDom;
34 using System.Web.Services;
35 using System.Web.Services.Protocols;
36 using System.Xml.Serialization;
37 using System.Xml;
38 using System.Xml.Schema;
39 using System.Collections;
40 using System.Configuration;
41
42 namespace System.Web.Services.Description {
43         public abstract class ProtocolImporter {
44
45                 #region Fields
46
47                 Binding binding;
48                 string className;
49                 CodeIdentifiers classNames;
50                 CodeNamespace codeNamespace;
51                 CodeCompileUnit codeCompileUnit;
52                 CodeTypeDeclaration codeTypeDeclaration;
53                 Message inputMessage;
54                 string methodName;
55                 Operation operation;
56                 OperationBinding operationBinding;
57                 Message outputMessage;          
58                 Port port;
59                 PortType portType;
60                 string protocolName;
61                 Service service;
62                 ServiceDescriptionImportWarnings warnings = (ServiceDescriptionImportWarnings)0;        
63                 ServiceDescriptionImporter descriptionImporter;
64                 ImportInfo iinfo;
65                 XmlSchemas xmlSchemas;
66                 XmlSchemas soapSchemas;
67
68                 #endregion // Fields
69
70                 #region Constructors
71         
72                 protected ProtocolImporter ()
73                 {
74                 }
75                 
76                 #endregion // Constructors
77
78                 #region Properties
79
80                 [MonoTODO]
81                 public XmlSchemas AbstractSchemas {
82                         get { return descriptionImporter.Schemas; }
83                 }
84
85                 public Binding Binding {
86                         get { return binding; }
87                 }
88
89                 public string ClassName {
90                         get { return className; }
91                 }
92
93                 public CodeIdentifiers ClassNames {
94                         get { return classNames; }
95                 }
96
97                 public CodeNamespace CodeNamespace {
98                         get { return codeNamespace; }
99                 }
100
101                 public CodeTypeDeclaration CodeTypeDeclaration {
102                         get { return codeTypeDeclaration; }
103                 }
104
105                 [MonoTODO]
106                 public XmlSchemas ConcreteSchemas {
107                         get { return descriptionImporter.Schemas; }
108                 }
109
110                 public Message InputMessage {
111                         get { return inputMessage; }
112                 }
113
114                 public string MethodName {
115                         get { return methodName; }
116                 }
117
118                 public Operation Operation {
119                         get { return operation; }
120                 }
121
122                 public OperationBinding OperationBinding {
123                         get { return operationBinding; }
124                 }
125
126                 public Message OutputMessage {
127                         get { return outputMessage; }
128                 }
129
130                 public Port Port {
131                         get { return port; }
132                 }
133
134                 public PortType PortType {
135                         get { return portType; }
136                 }
137
138                 public abstract string ProtocolName {
139                         get; 
140                 }
141
142                 public XmlSchemas Schemas {
143                         get { return descriptionImporter.Schemas; }
144                 }
145
146                 public Service Service {
147                         get { return service; } 
148                 }
149
150                 public ServiceDescriptionCollection ServiceDescriptions {
151                         get { return descriptionImporter.ServiceDescriptions; }
152                 }
153
154                 public ServiceDescriptionImportStyle Style {
155                         get { return descriptionImporter.Style; }
156                 }
157
158                 public ServiceDescriptionImportWarnings Warnings {
159                         get { return warnings; }
160                         set { warnings = value; }
161                 }
162                 
163                 internal ImportInfo ImportInfo
164                 {
165                         get { return iinfo; }
166                 }
167                 
168                 internal XmlSchemas LiteralSchemas
169                 {
170                         get { return xmlSchemas; }
171                 }
172                 
173                 internal XmlSchemas EncodedSchemas
174                 {
175                         get { return soapSchemas; }
176                 }
177
178                 #endregion // Properties
179
180                 #region Methods
181                 
182                 internal bool Import (ServiceDescriptionImporter descriptionImporter, CodeNamespace codeNamespace, CodeCompileUnit codeCompileUnit, ArrayList importInfo)
183                 {
184                         this.descriptionImporter = descriptionImporter;
185                         this.classNames = new CodeIdentifiers();;
186                         this.codeNamespace = codeNamespace;
187                         this.codeCompileUnit = codeCompileUnit;
188
189                         warnings = (ServiceDescriptionImportWarnings) 0;
190                         
191                         bool found = false;
192                         
193                         ClasifySchemas (importInfo);
194
195                         BeginNamespace ();
196                         
197                         foreach (ImportInfo info in importInfo)
198                         {
199                                 foreach (Service service in info.ServiceDescription.Services)
200                                 {
201                                         this.service = service;
202                                         int bindingCount = 0;
203                                         foreach (Port port in service.Ports)
204                                         {
205                                                 binding = ServiceDescriptions.GetBinding (port.Binding);
206                                                 if (IsBindingSupported ()) bindingCount ++;
207                                         }
208                                         
209                                         foreach (Port port in service.Ports)
210                                         {
211                                                 this.iinfo = info;
212                                                 this.port = port;
213                                                 binding = ServiceDescriptions.GetBinding (port.Binding);
214                                                 if (!IsBindingSupported ()) continue;
215                                                 
216                                                 found = true;
217                                                 ImportPortBinding (bindingCount > 1);
218                                         }
219                                 }
220                         }
221
222                         EndNamespace ();
223                         
224                         if (!found) warnings = ServiceDescriptionImportWarnings.NoCodeGenerated;
225                         return true;
226                 }
227
228                 void ImportPortBinding (bool multipleBindings)
229                 {
230                         if (multipleBindings) className = port.Name;
231                         else className = service.Name;
232                         
233                         className = classNames.AddUnique (CodeIdentifier.MakeValid (className), port);
234                         className = className.Replace ("_x0020_", "");  // MS.NET seems to do this
235                         
236                         try
237                         {
238                                 portType = ServiceDescriptions.GetPortType (binding.Type);
239                                 if (portType == null) throw new Exception ("Port type not found: " + binding.Type);
240
241                                 CodeTypeDeclaration codeClass = BeginClass ();
242                                 codeTypeDeclaration = codeClass;
243                                 AddCodeType (codeClass, port.Documentation);
244                                 codeClass.Attributes = MemberAttributes.Public;
245                                 
246                                 if (service.Documentation != null && service.Documentation != "")
247                                         AddComments (codeClass, service.Documentation);
248
249                                 CodeAttributeDeclaration att = new CodeAttributeDeclaration ("System.Diagnostics.DebuggerStepThroughAttribute");
250                                 AddCustomAttribute (codeClass, att, true);
251
252                                 att = new CodeAttributeDeclaration ("System.ComponentModel.DesignerCategoryAttribute");
253                                 att.Arguments.Add (GetArg ("code"));
254                                 AddCustomAttribute (codeClass, att, true);
255
256                                 if (binding.Operations.Count == 0) {
257                                         warnings |= ServiceDescriptionImportWarnings.NoMethodsGenerated;
258                                         return;
259                                 }
260                                 
261                                 foreach (OperationBinding oper in binding.Operations) 
262                                 {
263                                         operationBinding = oper;
264                                         operation = FindPortOperation ();
265                                         if (operation == null) throw new Exception ("Operation " + operationBinding.Name + " not found in portType " + PortType.Name);
266
267                                         foreach (OperationMessage omsg in operation.Messages)
268                                         {
269                                                 Message msg = ServiceDescriptions.GetMessage (omsg.Message);
270                                                 if (msg == null) throw new Exception ("Message not found: " + omsg.Message);
271                                                 
272                                                 if (omsg is OperationInput)
273                                                         inputMessage = msg;
274                                                 else
275                                                         outputMessage = msg;
276                                         }
277                                         
278                                         CodeMemberMethod method = GenerateMethod ();
279                                         
280                                         if (method != null)
281                                         {
282                                                 methodName = method.Name;
283                                                 if (operation.Documentation != null && operation.Documentation != "")
284                                                         AddComments (method, operation.Documentation);
285                                         }
286                                 }
287                                 
288                                 EndClass ();
289                         }
290                         catch (InvalidOperationException ex)
291                         {
292                                 warnings |= ServiceDescriptionImportWarnings.NoCodeGenerated;
293                                 UnsupportedBindingWarning (ex.Message);
294                         }
295                 }
296
297                 Operation FindPortOperation ()
298                 {
299                         string inMessage = null;
300                         string outMessage = null;
301                         int numMsg = 1;
302                         
303                         if (operationBinding.Input == null) throw new InvalidOperationException ("Input operation binding not found");
304                         inMessage = (operationBinding.Input.Name != null) ? operationBinding.Input.Name : operationBinding.Name;
305                                 
306                         if (operationBinding.Output != null) {
307                                 outMessage = (operationBinding.Output.Name != null) ? operationBinding.Output.Name : operationBinding.Name;
308                                 numMsg++;
309                         }
310                         
311                         string operName = operationBinding.Name;
312                         
313                         Operation foundOper = null;
314                         foreach (Operation oper in PortType.Operations)
315                         {
316                                 if (oper.Name == operName)
317                                 {
318                                         int hits = 0;
319                                         foreach (OperationMessage omsg in oper.Messages)
320                                         {
321                                                 if (omsg is OperationInput && GetOperMessageName (omsg, operName) == inMessage) hits++;
322                                                 if (omsg is OperationOutput && GetOperMessageName (omsg, operName) == outMessage) hits++;
323                                         }
324                                         if (hits == numMsg) return oper;
325                                         foundOper = oper;
326                                 }
327                         }
328                         return foundOper;
329                 }
330                 
331                 string GetOperMessageName (OperationMessage msg, string operName)
332                 {
333                         if (msg.Name == null) return operName;
334                         else return msg.Name;
335                 }
336                 
337                 internal string GetServiceUrl (string location)
338                 {
339                         if (ImportInfo.AppSettingUrlKey == null || ImportInfo.AppSettingUrlKey == string.Empty)
340                                 return location;
341                         else
342                         {
343                                 string url;
344                                 if (Style == ServiceDescriptionImportStyle.Server) throw new InvalidOperationException ("Cannot set appSettingUrlKey if Style is Server");
345                                 url = ConfigurationSettings.AppSettings [ImportInfo.AppSettingUrlKey];
346                                 if (ImportInfo.AppSettingBaseUrl != null && ImportInfo.AppSettingBaseUrl != string.Empty)
347                                         url += "/" + ImportInfo.AppSettingBaseUrl + "/" + location;
348                                 return url;
349                         }
350                 }
351
352                 void ClasifySchemas (ArrayList importInfo)
353                 {
354                         // I don't like this, but I could not find any other way of clasifying
355                         // schemas between encoded and literal.
356                         
357                         xmlSchemas = new XmlSchemas ();
358                         soapSchemas = new XmlSchemas ();
359                         
360                         foreach (ImportInfo info in importInfo)
361                         {
362                                 foreach (Service service in info.ServiceDescription.Services)
363                                 {
364                                         foreach (Port port in service.Ports)
365                                         {
366                                                 this.iinfo = info;
367                                                 this.port = port;
368                                                 binding = ServiceDescriptions.GetBinding (port.Binding);
369                                                 if (binding == null) continue;
370                                                 portType = ServiceDescriptions.GetPortType (binding.Type);
371                                                 if (portType == null) continue;
372                                                 
373                                                 foreach (OperationBinding oper in binding.Operations) 
374                                                 {
375                                                         operationBinding = oper;
376                                                         operation = FindPortOperation ();
377                                                         if (operation == null) continue;
378                 
379                                                         foreach (OperationMessage omsg in operation.Messages)
380                                                         {
381                                                                 Message msg = ServiceDescriptions.GetMessage (omsg.Message);
382                                                                 if (msg == null) continue;
383                                                                 
384                                                                 if (omsg is OperationInput)
385                                                                         inputMessage = msg;
386                                                                 else
387                                                                         outputMessage = msg;
388                                                         }
389                                                         
390                                                         if (GetMessageEncoding (oper.Input) == SoapBindingUse.Encoded)
391                                                                 AddMessageSchema (soapSchemas, oper.Input, inputMessage);
392                                                         else
393                                                                 AddMessageSchema (xmlSchemas, oper.Input, inputMessage);
394                                                         
395                                                         if (oper.Output != null) {
396                                                                 if (GetMessageEncoding (oper.Output) == SoapBindingUse.Encoded)
397                                                                         AddMessageSchema (soapSchemas, oper.Output, outputMessage);
398                                                                 else
399                                                                         AddMessageSchema (xmlSchemas, oper.Output, outputMessage);
400                                                         }
401                                                 }
402                                         }
403                                 }
404                         }
405                         
406                         XmlSchemas defaultList = xmlSchemas;
407                                 
408                         if (xmlSchemas.Count == 0 && soapSchemas.Count > 0)
409                                 defaultList = soapSchemas;
410                                 
411                         // Schemas not referenced by any message
412                         foreach (XmlSchema sc in Schemas)
413                         {
414                                 if (!soapSchemas.Contains (sc) && !xmlSchemas.Contains (sc)) {
415                                         if (ImportsEncodedNamespace (sc))
416                                                 soapSchemas.Add (sc);
417                                         else
418                                                 defaultList.Add (sc);
419                                 }
420                         }
421                 }
422                         
423                 void AddMessageSchema (XmlSchemas schemas, MessageBinding mb, Message msg)
424                 {
425                         foreach (MessagePart part in msg.Parts)
426                         {
427                                 if (part.Element != XmlQualifiedName.Empty)
428                                         AddIncludingSchema (schemas, part.Element.Namespace);
429                                 else if (part.Type != XmlQualifiedName.Empty)
430                                         AddIncludingSchema (schemas, part.Type.Namespace);
431                         }
432                         SoapBodyBinding sbb = mb.Extensions.Find (typeof(SoapBodyBinding)) as SoapBodyBinding;
433                         if (sbb != null) AddIncludingSchema (schemas, sbb.Namespace);
434                 }
435                 
436                 void AddIncludingSchema (XmlSchemas list, string ns)
437                 {
438                         XmlSchema sc = Schemas [ns];
439                         if (sc == null || list.Contains (sc)) return;
440                         list.Add (sc);
441                         foreach (XmlSchemaObject ob in sc.Includes)
442                         {
443                                 XmlSchemaImport import = ob as XmlSchemaImport;
444                                 if (import != null) AddIncludingSchema (list, import.Namespace);
445                         }
446                 }
447                 
448                 SoapBindingUse GetMessageEncoding (MessageBinding mb)
449                 {
450                         SoapBodyBinding sbb = mb.Extensions.Find (typeof(SoapBodyBinding)) as SoapBodyBinding;
451                         if (sbb == null)
452                         {
453                                 if (mb is InputBinding) return SoapBindingUse.Encoded;
454                                 else return SoapBindingUse.Literal;
455                         }
456                         else 
457                                 if (sbb.Use == SoapBindingUse.Encoded) return SoapBindingUse.Encoded;
458                         else
459                                 return SoapBindingUse.Literal;
460                 }
461                 
462                 bool ImportsEncodedNamespace (XmlSchema sc)
463                 {
464                         foreach (XmlSchemaObject ob in sc.Includes)
465                         {
466                                 XmlSchemaImport import = ob as XmlSchemaImport;
467                                 if (import.Namespace == SoapProtocolReflector.EncodingNamespace) return true;
468                         }
469                         return false;
470                 }
471                 
472                 [MonoTODO]
473                 public void AddExtensionWarningComments (CodeCommentStatementCollection comments, ServiceDescriptionFormatExtensionCollection extensions) 
474                 {
475                         throw new NotImplementedException ();
476                 }
477
478                 protected abstract CodeTypeDeclaration BeginClass ();
479
480                 protected virtual void BeginNamespace ()
481                 {
482                 }
483
484                 protected virtual void EndClass ()
485                 {
486                 }
487
488                 protected virtual void EndNamespace ()
489                 {
490                 }
491
492                 protected abstract CodeMemberMethod GenerateMethod ();
493                 protected abstract bool IsBindingSupported ();
494                 protected abstract bool IsOperationFlowSupported (OperationFlow flow);
495                 
496                 [MonoTODO]
497                 public Exception OperationBindingSyntaxException (string text)
498                 {
499                         throw new NotImplementedException ();
500                 }
501
502                 [MonoTODO]
503                 public Exception OperationSyntaxException (string text)
504                 {
505                         throw new NotImplementedException ();
506                 }
507
508                 public void UnsupportedBindingWarning (string text)
509                 {
510                         warnings |= ServiceDescriptionImportWarnings.UnsupportedBindingsIgnored;
511                         AddGlobalComments ("WARNING: Could not generate proxy for binding " + binding.Name + ". " + text);
512                 }
513
514                 public void UnsupportedOperationBindingWarning (string text)
515                 {
516                         warnings |= ServiceDescriptionImportWarnings.UnsupportedOperationsIgnored;
517                         AddGlobalComments ("WARNING: Could not generate operation " + OperationBinding.Name + ". " + text);
518                 }
519
520                 public void UnsupportedOperationWarning (string text)
521                 {
522                         warnings |= ServiceDescriptionImportWarnings.UnsupportedOperationsIgnored;
523                         AddGlobalComments ("WARNING: Could not generate operation " + OperationBinding.Name + ". " + text);
524                 }
525
526                 void AddGlobalComments (string comments)
527                 {
528                         codeNamespace.Comments.Add (new CodeCommentStatement (comments, false));
529                 }
530
531                 void AddComments (CodeTypeMember member, string comments)
532                 {
533                         if (comments == null || comments == "") member.Comments.Add (new CodeCommentStatement ("<remarks/>", true));
534                         else member.Comments.Add (new CodeCommentStatement ("<remarks>\n" + comments + "\n</remarks>", true));
535                 }
536
537                 void AddCodeType (CodeTypeDeclaration type, string comments)
538                 {
539                         AddComments (type, comments);
540                         codeNamespace.Types.Add (type);
541                 }
542
543                 internal void AddCustomAttribute (CodeTypeMember ctm, CodeAttributeDeclaration att, bool addIfNoParams)
544                 {
545                         if (att.Arguments.Count == 0 && !addIfNoParams) return;
546                         
547                         if (ctm.CustomAttributes == null) ctm.CustomAttributes = new CodeAttributeDeclarationCollection ();
548                         ctm.CustomAttributes.Add (att);
549                 }
550
551                 internal void AddCustomAttribute (CodeTypeMember ctm, string name, params CodeAttributeArgument[] args)
552                 {
553                         if (ctm.CustomAttributes == null) ctm.CustomAttributes = new CodeAttributeDeclarationCollection ();
554                         ctm.CustomAttributes.Add (new CodeAttributeDeclaration (name, args));
555                 }
556
557                 internal CodeAttributeArgument GetArg (string name, object value)
558                 {
559                         return new CodeAttributeArgument (name, new CodePrimitiveExpression(value));
560                 }
561
562                 internal CodeAttributeArgument GetEnumArg (string name, string enumType, string enumValue)
563                 {
564                         return new CodeAttributeArgument (name, new CodeFieldReferenceExpression (new CodeTypeReferenceExpression(enumType), enumValue));
565                 }
566
567                 internal CodeAttributeArgument GetArg (object value)
568                 {
569                         return new CodeAttributeArgument (new CodePrimitiveExpression(value));
570                 }
571
572                 internal CodeAttributeArgument GetTypeArg (string name, string typeName)
573                 {
574                         return new CodeAttributeArgument (name, new CodeTypeOfExpression(typeName));
575                 }
576                 
577                 #endregion
578         }
579 }