2 // System.Web.Services.Description.ProtocolImporter.cs
5 // Tim Coleman (tim@timcoleman.com)
6 // Lluis Sanchez Gual (lluis@ximian.com)
8 // Copyright (C) Tim Coleman, 2002
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:
20 // The above copyright notice and this permission notice shall be
21 // included in all copies or substantial portions of the Software.
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.
34 using System.CodeDom.Compiler;
35 using System.Web.Services;
36 using System.Web.Services.Protocols;
37 using System.Xml.Serialization;
39 using System.Xml.Schema;
40 using System.Collections;
41 using System.Configuration;
43 namespace System.Web.Services.Description {
44 public abstract class ProtocolImporter {
50 CodeIdentifiers classNames;
51 CodeNamespace codeNamespace;
52 CodeCompileUnit codeCompileUnit;
53 CodeTypeDeclaration codeTypeDeclaration;
57 OperationBinding operationBinding;
58 Message outputMessage;
63 ServiceDescriptionImportWarnings warnings = (ServiceDescriptionImportWarnings)0;
64 ServiceDescriptionImporter descriptionImporter;
66 XmlSchemas xmlSchemas;
67 XmlSchemas soapSchemas;
73 protected ProtocolImporter ()
77 #endregion // Constructors
82 public XmlSchemas AbstractSchemas {
83 get { return descriptionImporter.Schemas; }
86 public Binding Binding {
87 get { return binding; }
90 public string ClassName {
91 get { return className; }
94 public CodeIdentifiers ClassNames {
95 get { return classNames; }
98 public CodeNamespace CodeNamespace {
99 get { return codeNamespace; }
102 public CodeTypeDeclaration CodeTypeDeclaration {
103 get { return codeTypeDeclaration; }
107 public XmlSchemas ConcreteSchemas {
108 get { return descriptionImporter.Schemas; }
111 public Message InputMessage {
112 get { return inputMessage; }
115 public string MethodName {
116 get { return methodName; }
119 public Operation Operation {
120 get { return operation; }
123 public OperationBinding OperationBinding {
124 get { return operationBinding; }
127 public Message OutputMessage {
128 get { return outputMessage; }
135 public PortType PortType {
136 get { return portType; }
139 public abstract string ProtocolName {
143 public XmlSchemas Schemas {
144 get { return descriptionImporter.Schemas; }
147 public Service Service {
148 get { return service; }
151 public ServiceDescriptionCollection ServiceDescriptions {
152 get { return descriptionImporter.ServiceDescriptions; }
155 public ServiceDescriptionImportStyle Style {
156 get { return descriptionImporter.Style; }
159 public ServiceDescriptionImportWarnings Warnings {
160 get { return warnings; }
161 set { warnings = value; }
164 internal ImportInfo ImportInfo
166 get { return iinfo; }
169 internal XmlSchemas LiteralSchemas
171 get { return xmlSchemas; }
174 internal XmlSchemas EncodedSchemas
176 get { return soapSchemas; }
180 internal CodeGenerationOptions CodeGenerationOptions {
181 get { return descriptionImporter.CodeGenerationOptions; }
184 internal ICodeGenerator CodeGenerator {
185 get { return descriptionImporter.CodeGenerator; }
188 internal ImportContext ImportContext {
189 get { return descriptionImporter.Context; }
193 #endregion // Properties
197 internal bool Import (ServiceDescriptionImporter descriptionImporter, CodeNamespace codeNamespace, CodeCompileUnit codeCompileUnit, ArrayList importInfo)
199 this.descriptionImporter = descriptionImporter;
200 this.classNames = new CodeIdentifiers();;
201 this.codeNamespace = codeNamespace;
202 this.codeCompileUnit = codeCompileUnit;
204 warnings = (ServiceDescriptionImportWarnings) 0;
208 ClasifySchemas (importInfo);
212 foreach (ImportInfo info in importInfo)
214 foreach (Service service in info.ServiceDescription.Services)
216 this.service = service;
217 int bindingCount = 0;
218 foreach (Port port in service.Ports)
220 binding = ServiceDescriptions.GetBinding (port.Binding);
221 if (IsBindingSupported ()) bindingCount ++;
224 foreach (Port port in service.Ports)
228 binding = ServiceDescriptions.GetBinding (port.Binding);
229 if (!IsBindingSupported ()) continue;
232 ImportPortBinding (bindingCount > 1);
239 if (!found) warnings = ServiceDescriptionImportWarnings.NoCodeGenerated;
243 void ImportPortBinding (bool multipleBindings)
245 if (multipleBindings) className = port.Name;
246 else className = service.Name;
248 className = classNames.AddUnique (CodeIdentifier.MakeValid (className), port);
249 className = className.Replace ("_x0020_", ""); // MS.NET seems to do this
253 portType = ServiceDescriptions.GetPortType (binding.Type);
254 if (portType == null) throw new Exception ("Port type not found: " + binding.Type);
256 CodeTypeDeclaration codeClass = BeginClass ();
257 codeTypeDeclaration = codeClass;
258 AddCodeType (codeClass, port.Documentation);
259 codeClass.Attributes = MemberAttributes.Public;
261 if (service.Documentation != null && service.Documentation != "")
262 AddComments (codeClass, service.Documentation);
264 CodeAttributeDeclaration att = new CodeAttributeDeclaration ("System.Diagnostics.DebuggerStepThroughAttribute");
265 AddCustomAttribute (codeClass, att, true);
267 att = new CodeAttributeDeclaration ("System.ComponentModel.DesignerCategoryAttribute");
268 att.Arguments.Add (GetArg ("code"));
269 AddCustomAttribute (codeClass, att, true);
271 if (binding.Operations.Count == 0) {
272 warnings |= ServiceDescriptionImportWarnings.NoMethodsGenerated;
276 foreach (OperationBinding oper in binding.Operations)
278 operationBinding = oper;
279 operation = FindPortOperation ();
280 if (operation == null) throw new Exception ("Operation " + operationBinding.Name + " not found in portType " + PortType.Name);
282 foreach (OperationMessage omsg in operation.Messages)
284 Message msg = ServiceDescriptions.GetMessage (omsg.Message);
285 if (msg == null) throw new Exception ("Message not found: " + omsg.Message);
287 if (omsg is OperationInput)
293 CodeMemberMethod method = GenerateMethod ();
297 methodName = method.Name;
298 if (operation.Documentation != null && operation.Documentation != "")
299 AddComments (method, operation.Documentation);
305 catch (InvalidOperationException ex)
307 warnings |= ServiceDescriptionImportWarnings.NoCodeGenerated;
308 UnsupportedBindingWarning (ex.Message);
312 Operation FindPortOperation ()
314 string inMessage = null;
315 string outMessage = null;
318 if (operationBinding.Input == null) throw new InvalidOperationException ("Input operation binding not found");
319 inMessage = (operationBinding.Input.Name != null) ? operationBinding.Input.Name : operationBinding.Name;
321 if (operationBinding.Output != null) {
322 outMessage = (operationBinding.Output.Name != null) ? operationBinding.Output.Name : operationBinding.Name;
326 string operName = operationBinding.Name;
328 Operation foundOper = null;
329 foreach (Operation oper in PortType.Operations)
331 if (oper.Name == operName)
334 foreach (OperationMessage omsg in oper.Messages)
336 if (omsg is OperationInput && GetOperMessageName (omsg, operName) == inMessage) hits++;
337 if (omsg is OperationOutput && GetOperMessageName (omsg, operName) == outMessage) hits++;
339 if (hits == numMsg) return oper;
346 string GetOperMessageName (OperationMessage msg, string operName)
348 if (msg.Name == null) return operName;
349 else return msg.Name;
352 internal string GetServiceUrl (string location)
354 if (ImportInfo.AppSettingUrlKey == null || ImportInfo.AppSettingUrlKey == string.Empty)
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;
367 void ClasifySchemas (ArrayList importInfo)
369 // I don't like this, but I could not find any other way of clasifying
370 // schemas between encoded and literal.
372 xmlSchemas = new XmlSchemas ();
373 soapSchemas = new XmlSchemas ();
375 foreach (ImportInfo info in importInfo)
377 foreach (Service service in info.ServiceDescription.Services)
379 foreach (Port port in service.Ports)
383 binding = ServiceDescriptions.GetBinding (port.Binding);
384 if (binding == null) continue;
385 portType = ServiceDescriptions.GetPortType (binding.Type);
386 if (portType == null) continue;
388 foreach (OperationBinding oper in binding.Operations)
390 operationBinding = oper;
391 operation = FindPortOperation ();
392 if (operation == null) continue;
394 foreach (OperationMessage omsg in operation.Messages)
396 Message msg = ServiceDescriptions.GetMessage (omsg.Message);
397 if (msg == null) continue;
399 if (omsg is OperationInput)
405 if (GetMessageEncoding (oper.Input) == SoapBindingUse.Encoded)
406 AddMessageSchema (soapSchemas, oper.Input, inputMessage);
408 AddMessageSchema (xmlSchemas, oper.Input, inputMessage);
410 if (oper.Output != null) {
411 if (GetMessageEncoding (oper.Output) == SoapBindingUse.Encoded)
412 AddMessageSchema (soapSchemas, oper.Output, outputMessage);
414 AddMessageSchema (xmlSchemas, oper.Output, outputMessage);
421 XmlSchemas defaultList = xmlSchemas;
423 if (xmlSchemas.Count == 0 && soapSchemas.Count > 0)
424 defaultList = soapSchemas;
426 // Schemas not referenced by any message
427 foreach (XmlSchema sc in Schemas)
429 if (!soapSchemas.Contains (sc) && !xmlSchemas.Contains (sc)) {
430 if (ImportsEncodedNamespace (sc))
431 soapSchemas.Add (sc);
433 defaultList.Add (sc);
438 void AddMessageSchema (XmlSchemas schemas, MessageBinding mb, Message msg)
440 foreach (MessagePart part in msg.Parts)
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);
447 SoapBodyBinding sbb = mb.Extensions.Find (typeof(SoapBodyBinding)) as SoapBodyBinding;
448 if (sbb != null) AddIncludingSchema (schemas, sbb.Namespace);
451 void AddIncludingSchema (XmlSchemas list, string ns)
453 XmlSchema sc = Schemas [ns];
454 if (sc == null || list.Contains (sc)) return;
456 foreach (XmlSchemaObject ob in sc.Includes)
458 XmlSchemaImport import = ob as XmlSchemaImport;
459 if (import != null) AddIncludingSchema (list, import.Namespace);
463 SoapBindingUse GetMessageEncoding (MessageBinding mb)
465 SoapBodyBinding sbb = mb.Extensions.Find (typeof(SoapBodyBinding)) as SoapBodyBinding;
468 if (mb is InputBinding) return SoapBindingUse.Encoded;
469 else return SoapBindingUse.Literal;
472 if (sbb.Use == SoapBindingUse.Encoded) return SoapBindingUse.Encoded;
474 return SoapBindingUse.Literal;
477 bool ImportsEncodedNamespace (XmlSchema sc)
479 foreach (XmlSchemaObject ob in sc.Includes)
481 XmlSchemaImport import = ob as XmlSchemaImport;
482 if (import.Namespace == SoapProtocolReflector.EncodingNamespace) return true;
488 public void AddExtensionWarningComments (CodeCommentStatementCollection comments, ServiceDescriptionFormatExtensionCollection extensions)
490 throw new NotImplementedException ();
493 protected abstract CodeTypeDeclaration BeginClass ();
495 protected virtual void BeginNamespace ()
499 protected virtual void EndClass ()
503 protected virtual void EndNamespace ()
507 protected abstract CodeMemberMethod GenerateMethod ();
508 protected abstract bool IsBindingSupported ();
509 protected abstract bool IsOperationFlowSupported (OperationFlow flow);
512 public Exception OperationBindingSyntaxException (string text)
514 throw new NotImplementedException ();
518 public Exception OperationSyntaxException (string text)
520 throw new NotImplementedException ();
523 public void UnsupportedBindingWarning (string text)
525 warnings |= ServiceDescriptionImportWarnings.UnsupportedBindingsIgnored;
526 AddGlobalComments ("WARNING: Could not generate proxy for binding " + binding.Name + ". " + text);
529 public void UnsupportedOperationBindingWarning (string text)
531 warnings |= ServiceDescriptionImportWarnings.UnsupportedOperationsIgnored;
532 AddGlobalComments ("WARNING: Could not generate operation " + OperationBinding.Name + ". " + text);
535 public void UnsupportedOperationWarning (string text)
537 warnings |= ServiceDescriptionImportWarnings.UnsupportedOperationsIgnored;
538 AddGlobalComments ("WARNING: Could not generate operation " + OperationBinding.Name + ". " + text);
541 void AddGlobalComments (string comments)
543 codeNamespace.Comments.Add (new CodeCommentStatement (comments, false));
546 void AddComments (CodeTypeMember member, string comments)
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));
552 void AddCodeType (CodeTypeDeclaration type, string comments)
554 AddComments (type, comments);
555 codeNamespace.Types.Add (type);
558 internal void AddCustomAttribute (CodeTypeMember ctm, CodeAttributeDeclaration att, bool addIfNoParams)
560 if (att.Arguments.Count == 0 && !addIfNoParams) return;
562 if (ctm.CustomAttributes == null) ctm.CustomAttributes = new CodeAttributeDeclarationCollection ();
563 ctm.CustomAttributes.Add (att);
566 internal void AddCustomAttribute (CodeTypeMember ctm, string name, params CodeAttributeArgument[] args)
568 if (ctm.CustomAttributes == null) ctm.CustomAttributes = new CodeAttributeDeclarationCollection ();
569 ctm.CustomAttributes.Add (new CodeAttributeDeclaration (name, args));
572 internal CodeAttributeArgument GetArg (string name, object value)
574 return new CodeAttributeArgument (name, new CodePrimitiveExpression(value));
577 internal CodeAttributeArgument GetEnumArg (string name, string enumType, string enumValue)
579 return new CodeAttributeArgument (name, new CodeFieldReferenceExpression (new CodeTypeReferenceExpression(enumType), enumValue));
582 internal CodeAttributeArgument GetArg (object value)
584 return new CodeAttributeArgument (new CodePrimitiveExpression(value));
587 internal CodeAttributeArgument GetTypeArg (string name, string typeName)
589 return new CodeAttributeArgument (name, new CodeTypeOfExpression(typeName));