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