//
// System.Web.Services.Description.ProtocolImporter.cs
//
// Author:
// Tim Coleman (tim@timcoleman.com)
// Lluis Sanchez Gual (lluis@ximian.com)
//
// Copyright (C) Tim Coleman, 2002
//
//
// Permission is hereby granted, free of charge, to any person obtaining
// a copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to
// permit persons to whom the Software is furnished to do so, subject to
// the following conditions:
//
// The above copyright notice and this permission notice shall be
// included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
//
using System;
using System.CodeDom;
using System.Web.Services;
using System.Web.Services.Protocols;
using System.Xml.Serialization;
using System.Xml;
using System.Xml.Schema;
using System.Collections;
using System.Configuration;
namespace System.Web.Services.Description {
public abstract class ProtocolImporter {
#region Fields
Binding binding;
string className;
CodeIdentifiers classNames;
CodeNamespace codeNamespace;
CodeCompileUnit codeCompileUnit;
CodeTypeDeclaration codeTypeDeclaration;
Message inputMessage;
string methodName;
Operation operation;
OperationBinding operationBinding;
Message outputMessage;
Port port;
PortType portType;
string protocolName;
Service service;
ServiceDescriptionImportWarnings warnings = (ServiceDescriptionImportWarnings)0;
ServiceDescriptionImporter descriptionImporter;
ImportInfo iinfo;
XmlSchemas xmlSchemas;
XmlSchemas soapSchemas;
#endregion // Fields
#region Constructors
protected ProtocolImporter ()
{
}
#endregion // Constructors
#region Properties
[MonoTODO]
public XmlSchemas AbstractSchemas {
get { return descriptionImporter.Schemas; }
}
public Binding Binding {
get { return binding; }
}
public string ClassName {
get { return className; }
}
public CodeIdentifiers ClassNames {
get { return classNames; }
}
public CodeNamespace CodeNamespace {
get { return codeNamespace; }
}
public CodeTypeDeclaration CodeTypeDeclaration {
get { return codeTypeDeclaration; }
}
[MonoTODO]
public XmlSchemas ConcreteSchemas {
get { return descriptionImporter.Schemas; }
}
public Message InputMessage {
get { return inputMessage; }
}
public string MethodName {
get { return methodName; }
}
public Operation Operation {
get { return operation; }
}
public OperationBinding OperationBinding {
get { return operationBinding; }
}
public Message OutputMessage {
get { return outputMessage; }
}
public Port Port {
get { return port; }
}
public PortType PortType {
get { return portType; }
}
public abstract string ProtocolName {
get;
}
public XmlSchemas Schemas {
get { return descriptionImporter.Schemas; }
}
public Service Service {
get { return service; }
}
public ServiceDescriptionCollection ServiceDescriptions {
get { return descriptionImporter.ServiceDescriptions; }
}
public ServiceDescriptionImportStyle Style {
get { return descriptionImporter.Style; }
}
public ServiceDescriptionImportWarnings Warnings {
get { return warnings; }
set { warnings = value; }
}
internal ImportInfo ImportInfo
{
get { return iinfo; }
}
internal XmlSchemas LiteralSchemas
{
get { return xmlSchemas; }
}
internal XmlSchemas EncodedSchemas
{
get { return soapSchemas; }
}
#endregion // Properties
#region Methods
internal bool Import (ServiceDescriptionImporter descriptionImporter, CodeNamespace codeNamespace, CodeCompileUnit codeCompileUnit, ArrayList importInfo)
{
this.descriptionImporter = descriptionImporter;
this.classNames = new CodeIdentifiers();;
this.codeNamespace = codeNamespace;
this.codeCompileUnit = codeCompileUnit;
warnings = (ServiceDescriptionImportWarnings) 0;
bool found = false;
ClasifySchemas (importInfo);
BeginNamespace ();
foreach (ImportInfo info in importInfo)
{
foreach (Service service in info.ServiceDescription.Services)
{
this.service = service;
int bindingCount = 0;
foreach (Port port in service.Ports)
{
binding = ServiceDescriptions.GetBinding (port.Binding);
if (IsBindingSupported ()) bindingCount ++;
}
foreach (Port port in service.Ports)
{
this.iinfo = info;
this.port = port;
binding = ServiceDescriptions.GetBinding (port.Binding);
if (!IsBindingSupported ()) continue;
found = true;
ImportPortBinding (bindingCount > 1);
}
}
}
EndNamespace ();
if (!found) warnings = ServiceDescriptionImportWarnings.NoCodeGenerated;
return true;
}
void ImportPortBinding (bool multipleBindings)
{
if (multipleBindings) className = port.Name;
else className = service.Name;
className = classNames.AddUnique (CodeIdentifier.MakeValid (className), port);
className = className.Replace ("_x0020_", ""); // MS.NET seems to do this
try
{
portType = ServiceDescriptions.GetPortType (binding.Type);
if (portType == null) throw new Exception ("Port type not found: " + binding.Type);
CodeTypeDeclaration codeClass = BeginClass ();
codeTypeDeclaration = codeClass;
AddCodeType (codeClass, port.Documentation);
codeClass.Attributes = MemberAttributes.Public;
if (service.Documentation != null && service.Documentation != "")
AddComments (codeClass, service.Documentation);
CodeAttributeDeclaration att = new CodeAttributeDeclaration ("System.Diagnostics.DebuggerStepThroughAttribute");
AddCustomAttribute (codeClass, att, true);
att = new CodeAttributeDeclaration ("System.ComponentModel.DesignerCategoryAttribute");
att.Arguments.Add (GetArg ("code"));
AddCustomAttribute (codeClass, att, true);
if (binding.Operations.Count == 0) {
warnings |= ServiceDescriptionImportWarnings.NoMethodsGenerated;
return;
}
foreach (OperationBinding oper in binding.Operations)
{
operationBinding = oper;
operation = FindPortOperation ();
if (operation == null) throw new Exception ("Operation " + operationBinding.Name + " not found in portType " + PortType.Name);
foreach (OperationMessage omsg in operation.Messages)
{
Message msg = ServiceDescriptions.GetMessage (omsg.Message);
if (msg == null) throw new Exception ("Message not found: " + omsg.Message);
if (omsg is OperationInput)
inputMessage = msg;
else
outputMessage = msg;
}
CodeMemberMethod method = GenerateMethod ();
if (method != null)
{
methodName = method.Name;
if (operation.Documentation != null && operation.Documentation != "")
AddComments (method, operation.Documentation);
}
}
EndClass ();
}
catch (InvalidOperationException ex)
{
warnings |= ServiceDescriptionImportWarnings.NoCodeGenerated;
UnsupportedBindingWarning (ex.Message);
}
}
Operation FindPortOperation ()
{
string inMessage = null;
string outMessage = null;
int numMsg = 1;
if (operationBinding.Input == null) throw new InvalidOperationException ("Input operation binding not found");
inMessage = (operationBinding.Input.Name != null) ? operationBinding.Input.Name : operationBinding.Name;
if (operationBinding.Output != null) {
outMessage = (operationBinding.Output.Name != null) ? operationBinding.Output.Name : operationBinding.Name;
numMsg++;
}
string operName = operationBinding.Name;
Operation foundOper = null;
foreach (Operation oper in PortType.Operations)
{
if (oper.Name == operName)
{
int hits = 0;
foreach (OperationMessage omsg in oper.Messages)
{
if (omsg is OperationInput && GetOperMessageName (omsg, operName) == inMessage) hits++;
if (omsg is OperationOutput && GetOperMessageName (omsg, operName) == outMessage) hits++;
}
if (hits == numMsg) return oper;
foundOper = oper;
}
}
return foundOper;
}
string GetOperMessageName (OperationMessage msg, string operName)
{
if (msg.Name == null) return operName;
else return msg.Name;
}
internal string GetServiceUrl (string location)
{
if (ImportInfo.AppSettingUrlKey == null || ImportInfo.AppSettingUrlKey == string.Empty)
return location;
else
{
string url;
if (Style == ServiceDescriptionImportStyle.Server) throw new InvalidOperationException ("Cannot set appSettingUrlKey if Style is Server");
url = ConfigurationSettings.AppSettings [ImportInfo.AppSettingUrlKey];
if (ImportInfo.AppSettingBaseUrl != null && ImportInfo.AppSettingBaseUrl != string.Empty)
url += "/" + ImportInfo.AppSettingBaseUrl + "/" + location;
return url;
}
}
void ClasifySchemas (ArrayList importInfo)
{
// I don't like this, but I could not find any other way of clasifying
// schemas between encoded and literal.
xmlSchemas = new XmlSchemas ();
soapSchemas = new XmlSchemas ();
foreach (ImportInfo info in importInfo)
{
foreach (Service service in info.ServiceDescription.Services)
{
foreach (Port port in service.Ports)
{
this.iinfo = info;
this.port = port;
binding = ServiceDescriptions.GetBinding (port.Binding);
if (binding == null) continue;
portType = ServiceDescriptions.GetPortType (binding.Type);
if (portType == null) continue;
foreach (OperationBinding oper in binding.Operations)
{
operationBinding = oper;
operation = FindPortOperation ();
if (operation == null) continue;
foreach (OperationMessage omsg in operation.Messages)
{
Message msg = ServiceDescriptions.GetMessage (omsg.Message);
if (msg == null) continue;
if (omsg is OperationInput)
inputMessage = msg;
else
outputMessage = msg;
}
if (GetMessageEncoding (oper.Input) == SoapBindingUse.Encoded)
AddMessageSchema (soapSchemas, oper.Input, inputMessage);
else
AddMessageSchema (xmlSchemas, oper.Input, inputMessage);
if (oper.Output != null) {
if (GetMessageEncoding (oper.Output) == SoapBindingUse.Encoded)
AddMessageSchema (soapSchemas, oper.Output, outputMessage);
else
AddMessageSchema (xmlSchemas, oper.Output, outputMessage);
}
}
}
}
}
XmlSchemas defaultList = xmlSchemas;
if (xmlSchemas.Count == 0 && soapSchemas.Count > 0)
defaultList = soapSchemas;
// Schemas not referenced by any message
foreach (XmlSchema sc in Schemas)
{
if (!soapSchemas.Contains (sc) && !xmlSchemas.Contains (sc)) {
if (ImportsEncodedNamespace (sc))
soapSchemas.Add (sc);
else
defaultList.Add (sc);
}
}
}
void AddMessageSchema (XmlSchemas schemas, MessageBinding mb, Message msg)
{
foreach (MessagePart part in msg.Parts)
{
if (part.Element != XmlQualifiedName.Empty)
AddIncludingSchema (schemas, part.Element.Namespace);
else if (part.Type != XmlQualifiedName.Empty)
AddIncludingSchema (schemas, part.Type.Namespace);
}
SoapBodyBinding sbb = mb.Extensions.Find (typeof(SoapBodyBinding)) as SoapBodyBinding;
if (sbb != null) AddIncludingSchema (schemas, sbb.Namespace);
}
void AddIncludingSchema (XmlSchemas list, string ns)
{
XmlSchema sc = Schemas [ns];
if (sc == null || list.Contains (sc)) return;
list.Add (sc);
foreach (XmlSchemaObject ob in sc.Includes)
{
XmlSchemaImport import = ob as XmlSchemaImport;
if (import != null) AddIncludingSchema (list, import.Namespace);
}
}
SoapBindingUse GetMessageEncoding (MessageBinding mb)
{
SoapBodyBinding sbb = mb.Extensions.Find (typeof(SoapBodyBinding)) as SoapBodyBinding;
if (sbb == null)
{
if (mb is InputBinding) return SoapBindingUse.Encoded;
else return SoapBindingUse.Literal;
}
else
if (sbb.Use == SoapBindingUse.Encoded) return SoapBindingUse.Encoded;
else
return SoapBindingUse.Literal;
}
bool ImportsEncodedNamespace (XmlSchema sc)
{
foreach (XmlSchemaObject ob in sc.Includes)
{
XmlSchemaImport import = ob as XmlSchemaImport;
if (import.Namespace == SoapProtocolReflector.EncodingNamespace) return true;
}
return false;
}
[MonoTODO]
public void AddExtensionWarningComments (CodeCommentStatementCollection comments, ServiceDescriptionFormatExtensionCollection extensions)
{
throw new NotImplementedException ();
}
protected abstract CodeTypeDeclaration BeginClass ();
protected virtual void BeginNamespace ()
{
}
protected virtual void EndClass ()
{
}
protected virtual void EndNamespace ()
{
}
protected abstract CodeMemberMethod GenerateMethod ();
protected abstract bool IsBindingSupported ();
protected abstract bool IsOperationFlowSupported (OperationFlow flow);
[MonoTODO]
public Exception OperationBindingSyntaxException (string text)
{
throw new NotImplementedException ();
}
[MonoTODO]
public Exception OperationSyntaxException (string text)
{
throw new NotImplementedException ();
}
public void UnsupportedBindingWarning (string text)
{
warnings |= ServiceDescriptionImportWarnings.UnsupportedBindingsIgnored;
AddGlobalComments ("WARNING: Could not generate proxy for binding " + binding.Name + ". " + text);
}
public void UnsupportedOperationBindingWarning (string text)
{
warnings |= ServiceDescriptionImportWarnings.UnsupportedOperationsIgnored;
AddGlobalComments ("WARNING: Could not generate operation " + OperationBinding.Name + ". " + text);
}
public void UnsupportedOperationWarning (string text)
{
warnings |= ServiceDescriptionImportWarnings.UnsupportedOperationsIgnored;
AddGlobalComments ("WARNING: Could not generate operation " + OperationBinding.Name + ". " + text);
}
void AddGlobalComments (string comments)
{
codeNamespace.Comments.Add (new CodeCommentStatement (comments, false));
}
void AddComments (CodeTypeMember member, string comments)
{
if (comments == null || comments == "") member.Comments.Add (new CodeCommentStatement ("", true));
else member.Comments.Add (new CodeCommentStatement ("\n" + comments + "\n", true));
}
void AddCodeType (CodeTypeDeclaration type, string comments)
{
AddComments (type, comments);
codeNamespace.Types.Add (type);
}
internal void AddCustomAttribute (CodeTypeMember ctm, CodeAttributeDeclaration att, bool addIfNoParams)
{
if (att.Arguments.Count == 0 && !addIfNoParams) return;
if (ctm.CustomAttributes == null) ctm.CustomAttributes = new CodeAttributeDeclarationCollection ();
ctm.CustomAttributes.Add (att);
}
internal void AddCustomAttribute (CodeTypeMember ctm, string name, params CodeAttributeArgument[] args)
{
if (ctm.CustomAttributes == null) ctm.CustomAttributes = new CodeAttributeDeclarationCollection ();
ctm.CustomAttributes.Add (new CodeAttributeDeclaration (name, args));
}
internal CodeAttributeArgument GetArg (string name, object value)
{
return new CodeAttributeArgument (name, new CodePrimitiveExpression(value));
}
internal CodeAttributeArgument GetEnumArg (string name, string enumType, string enumValue)
{
return new CodeAttributeArgument (name, new CodeFieldReferenceExpression (new CodeTypeReferenceExpression(enumType), enumValue));
}
internal CodeAttributeArgument GetArg (object value)
{
return new CodeAttributeArgument (new CodePrimitiveExpression(value));
}
internal CodeAttributeArgument GetTypeArg (string name, string typeName)
{
return new CodeAttributeArgument (name, new CodeTypeOfExpression(typeName));
}
#endregion
}
}