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