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.Web.Services;
35 using System.Web.Services.Protocols;
36 using System.Xml.Serialization;
38 using System.Xml.Schema;
39 using System.Collections;
40 using System.Configuration;
42 namespace System.Web.Services.Description {
43 public abstract class ProtocolImporter {
49 CodeIdentifiers classNames;
50 CodeNamespace codeNamespace;
51 CodeCompileUnit codeCompileUnit;
52 CodeTypeDeclaration codeTypeDeclaration;
56 OperationBinding operationBinding;
57 Message outputMessage;
62 ServiceDescriptionImportWarnings warnings = (ServiceDescriptionImportWarnings)0;
63 ServiceDescriptionImporter descriptionImporter;
65 XmlSchemas xmlSchemas;
66 XmlSchemas soapSchemas;
72 protected ProtocolImporter ()
76 #endregion // Constructors
81 public XmlSchemas AbstractSchemas {
82 get { return descriptionImporter.Schemas; }
85 public Binding Binding {
86 get { return binding; }
89 public string ClassName {
90 get { return className; }
93 public CodeIdentifiers ClassNames {
94 get { return classNames; }
97 public CodeNamespace CodeNamespace {
98 get { return codeNamespace; }
101 public CodeTypeDeclaration CodeTypeDeclaration {
102 get { return codeTypeDeclaration; }
106 public XmlSchemas ConcreteSchemas {
107 get { return descriptionImporter.Schemas; }
110 public Message InputMessage {
111 get { return inputMessage; }
114 public string MethodName {
115 get { return methodName; }
118 public Operation Operation {
119 get { return operation; }
122 public OperationBinding OperationBinding {
123 get { return operationBinding; }
126 public Message OutputMessage {
127 get { return outputMessage; }
134 public PortType PortType {
135 get { return portType; }
138 public abstract string ProtocolName {
142 public XmlSchemas Schemas {
143 get { return descriptionImporter.Schemas; }
146 public Service Service {
147 get { return service; }
150 public ServiceDescriptionCollection ServiceDescriptions {
151 get { return descriptionImporter.ServiceDescriptions; }
154 public ServiceDescriptionImportStyle Style {
155 get { return descriptionImporter.Style; }
158 public ServiceDescriptionImportWarnings Warnings {
159 get { return warnings; }
160 set { warnings = value; }
163 internal ImportInfo ImportInfo
165 get { return iinfo; }
168 internal XmlSchemas LiteralSchemas
170 get { return xmlSchemas; }
173 internal XmlSchemas EncodedSchemas
175 get { return soapSchemas; }
178 #endregion // Properties
182 internal bool Import (ServiceDescriptionImporter descriptionImporter, CodeNamespace codeNamespace, CodeCompileUnit codeCompileUnit, ArrayList importInfo)
184 this.descriptionImporter = descriptionImporter;
185 this.classNames = new CodeIdentifiers();;
186 this.codeNamespace = codeNamespace;
187 this.codeCompileUnit = codeCompileUnit;
189 warnings = (ServiceDescriptionImportWarnings) 0;
193 ClasifySchemas (importInfo);
197 foreach (ImportInfo info in importInfo)
199 foreach (Service service in info.ServiceDescription.Services)
201 this.service = service;
202 int bindingCount = 0;
203 foreach (Port port in service.Ports)
205 binding = ServiceDescriptions.GetBinding (port.Binding);
206 if (IsBindingSupported ()) bindingCount ++;
209 foreach (Port port in service.Ports)
213 binding = ServiceDescriptions.GetBinding (port.Binding);
214 if (!IsBindingSupported ()) continue;
217 ImportPortBinding (bindingCount > 1);
224 if (!found) warnings = ServiceDescriptionImportWarnings.NoCodeGenerated;
228 void ImportPortBinding (bool multipleBindings)
230 if (multipleBindings) className = port.Name;
231 else className = service.Name;
233 className = classNames.AddUnique (CodeIdentifier.MakeValid (className), port);
234 className = className.Replace ("_x0020_", ""); // MS.NET seems to do this
238 portType = ServiceDescriptions.GetPortType (binding.Type);
239 if (portType == null) throw new Exception ("Port type not found: " + binding.Type);
241 CodeTypeDeclaration codeClass = BeginClass ();
242 codeTypeDeclaration = codeClass;
243 AddCodeType (codeClass, port.Documentation);
244 codeClass.Attributes = MemberAttributes.Public;
246 if (service.Documentation != null && service.Documentation != "")
247 AddComments (codeClass, service.Documentation);
249 CodeAttributeDeclaration att = new CodeAttributeDeclaration ("System.Diagnostics.DebuggerStepThroughAttribute");
250 AddCustomAttribute (codeClass, att, true);
252 att = new CodeAttributeDeclaration ("System.ComponentModel.DesignerCategoryAttribute");
253 att.Arguments.Add (GetArg ("code"));
254 AddCustomAttribute (codeClass, att, true);
256 if (binding.Operations.Count == 0) {
257 warnings |= ServiceDescriptionImportWarnings.NoMethodsGenerated;
261 foreach (OperationBinding oper in binding.Operations)
263 operationBinding = oper;
264 operation = FindPortOperation ();
265 if (operation == null) throw new Exception ("Operation " + operationBinding.Name + " not found in portType " + PortType.Name);
267 foreach (OperationMessage omsg in operation.Messages)
269 Message msg = ServiceDescriptions.GetMessage (omsg.Message);
270 if (msg == null) throw new Exception ("Message not found: " + omsg.Message);
272 if (omsg is OperationInput)
278 CodeMemberMethod method = GenerateMethod ();
282 methodName = method.Name;
283 if (operation.Documentation != null && operation.Documentation != "")
284 AddComments (method, operation.Documentation);
290 catch (InvalidOperationException ex)
292 warnings |= ServiceDescriptionImportWarnings.NoCodeGenerated;
293 UnsupportedBindingWarning (ex.Message);
297 Operation FindPortOperation ()
299 string inMessage = null;
300 string outMessage = null;
303 if (operationBinding.Input == null) throw new InvalidOperationException ("Input operation binding not found");
304 inMessage = (operationBinding.Input.Name != null) ? operationBinding.Input.Name : operationBinding.Name;
306 if (operationBinding.Output != null) {
307 outMessage = (operationBinding.Output.Name != null) ? operationBinding.Output.Name : operationBinding.Name;
311 string operName = operationBinding.Name;
313 Operation foundOper = null;
314 foreach (Operation oper in PortType.Operations)
316 if (oper.Name == operName)
319 foreach (OperationMessage omsg in oper.Messages)
321 if (omsg is OperationInput && GetOperMessageName (omsg, operName) == inMessage) hits++;
322 if (omsg is OperationOutput && GetOperMessageName (omsg, operName) == outMessage) hits++;
324 if (hits == numMsg) return oper;
331 string GetOperMessageName (OperationMessage msg, string operName)
333 if (msg.Name == null) return operName;
334 else return msg.Name;
337 internal string GetServiceUrl (string location)
339 if (ImportInfo.AppSettingUrlKey == null || ImportInfo.AppSettingUrlKey == string.Empty)
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;
352 void ClasifySchemas (ArrayList importInfo)
354 // I don't like this, but I could not find any other way of clasifying
355 // schemas between encoded and literal.
357 xmlSchemas = new XmlSchemas ();
358 soapSchemas = new XmlSchemas ();
360 foreach (ImportInfo info in importInfo)
362 foreach (Service service in info.ServiceDescription.Services)
364 foreach (Port port in service.Ports)
368 binding = ServiceDescriptions.GetBinding (port.Binding);
369 if (binding == null) continue;
370 portType = ServiceDescriptions.GetPortType (binding.Type);
371 if (portType == null) continue;
373 foreach (OperationBinding oper in binding.Operations)
375 operationBinding = oper;
376 operation = FindPortOperation ();
377 if (operation == null) continue;
379 foreach (OperationMessage omsg in operation.Messages)
381 Message msg = ServiceDescriptions.GetMessage (omsg.Message);
382 if (msg == null) continue;
384 if (omsg is OperationInput)
390 if (GetMessageEncoding (oper.Input) == SoapBindingUse.Encoded)
391 AddMessageSchema (soapSchemas, oper.Input, inputMessage);
393 AddMessageSchema (xmlSchemas, oper.Input, inputMessage);
395 if (oper.Output != null) {
396 if (GetMessageEncoding (oper.Output) == SoapBindingUse.Encoded)
397 AddMessageSchema (soapSchemas, oper.Output, outputMessage);
399 AddMessageSchema (xmlSchemas, oper.Output, outputMessage);
406 XmlSchemas defaultList = xmlSchemas;
408 if (xmlSchemas.Count == 0 && soapSchemas.Count > 0)
409 defaultList = soapSchemas;
411 // Schemas not referenced by any message
412 foreach (XmlSchema sc in Schemas)
414 if (!soapSchemas.Contains (sc) && !xmlSchemas.Contains (sc)) {
415 if (ImportsEncodedNamespace (sc))
416 soapSchemas.Add (sc);
418 defaultList.Add (sc);
423 void AddMessageSchema (XmlSchemas schemas, MessageBinding mb, Message msg)
425 foreach (MessagePart part in msg.Parts)
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);
432 SoapBodyBinding sbb = mb.Extensions.Find (typeof(SoapBodyBinding)) as SoapBodyBinding;
433 if (sbb != null) AddIncludingSchema (schemas, sbb.Namespace);
436 void AddIncludingSchema (XmlSchemas list, string ns)
438 XmlSchema sc = Schemas [ns];
439 if (sc == null || list.Contains (sc)) return;
441 foreach (XmlSchemaObject ob in sc.Includes)
443 XmlSchemaImport import = ob as XmlSchemaImport;
444 if (import != null) AddIncludingSchema (list, import.Namespace);
448 SoapBindingUse GetMessageEncoding (MessageBinding mb)
450 SoapBodyBinding sbb = mb.Extensions.Find (typeof(SoapBodyBinding)) as SoapBodyBinding;
453 if (mb is InputBinding) return SoapBindingUse.Encoded;
454 else return SoapBindingUse.Literal;
457 if (sbb.Use == SoapBindingUse.Encoded) return SoapBindingUse.Encoded;
459 return SoapBindingUse.Literal;
462 bool ImportsEncodedNamespace (XmlSchema sc)
464 foreach (XmlSchemaObject ob in sc.Includes)
466 XmlSchemaImport import = ob as XmlSchemaImport;
467 if (import.Namespace == SoapProtocolReflector.EncodingNamespace) return true;
473 public void AddExtensionWarningComments (CodeCommentStatementCollection comments, ServiceDescriptionFormatExtensionCollection extensions)
475 throw new NotImplementedException ();
478 protected abstract CodeTypeDeclaration BeginClass ();
480 protected virtual void BeginNamespace ()
484 protected virtual void EndClass ()
488 protected virtual void EndNamespace ()
492 protected abstract CodeMemberMethod GenerateMethod ();
493 protected abstract bool IsBindingSupported ();
494 protected abstract bool IsOperationFlowSupported (OperationFlow flow);
497 public Exception OperationBindingSyntaxException (string text)
499 throw new NotImplementedException ();
503 public Exception OperationSyntaxException (string text)
505 throw new NotImplementedException ();
508 public void UnsupportedBindingWarning (string text)
510 warnings |= ServiceDescriptionImportWarnings.UnsupportedBindingsIgnored;
511 AddGlobalComments ("WARNING: Could not generate proxy for binding " + binding.Name + ". " + text);
514 public void UnsupportedOperationBindingWarning (string text)
516 warnings |= ServiceDescriptionImportWarnings.UnsupportedOperationsIgnored;
517 AddGlobalComments ("WARNING: Could not generate operation " + OperationBinding.Name + ". " + text);
520 public void UnsupportedOperationWarning (string text)
522 warnings |= ServiceDescriptionImportWarnings.UnsupportedOperationsIgnored;
523 AddGlobalComments ("WARNING: Could not generate operation " + OperationBinding.Name + ". " + text);
526 void AddGlobalComments (string comments)
528 codeNamespace.Comments.Add (new CodeCommentStatement (comments, false));
531 void AddComments (CodeTypeMember member, string comments)
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));
537 void AddCodeType (CodeTypeDeclaration type, string comments)
539 AddComments (type, comments);
540 codeNamespace.Types.Add (type);
543 internal void AddCustomAttribute (CodeTypeMember ctm, CodeAttributeDeclaration att, bool addIfNoParams)
545 if (att.Arguments.Count == 0 && !addIfNoParams) return;
547 if (ctm.CustomAttributes == null) ctm.CustomAttributes = new CodeAttributeDeclarationCollection ();
548 ctm.CustomAttributes.Add (att);
551 internal void AddCustomAttribute (CodeTypeMember ctm, string name, params CodeAttributeArgument[] args)
553 if (ctm.CustomAttributes == null) ctm.CustomAttributes = new CodeAttributeDeclarationCollection ();
554 ctm.CustomAttributes.Add (new CodeAttributeDeclaration (name, args));
557 internal CodeAttributeArgument GetArg (string name, object value)
559 return new CodeAttributeArgument (name, new CodePrimitiveExpression(value));
562 internal CodeAttributeArgument GetEnumArg (string name, string enumType, string enumValue)
564 return new CodeAttributeArgument (name, new CodeFieldReferenceExpression (new CodeTypeReferenceExpression(enumType), enumValue));
567 internal CodeAttributeArgument GetArg (object value)
569 return new CodeAttributeArgument (new CodePrimitiveExpression(value));
572 internal CodeAttributeArgument GetTypeArg (string name, string typeName)
574 return new CodeAttributeArgument (name, new CodeTypeOfExpression(typeName));