linux-packaging-mono/mcs/tools/svcutil/MoonlightChannelBaseExtension.cs
Jo Shields a575963da9 Imported Upstream version 3.6.0
Former-commit-id: da6be194a6b1221998fc28233f2503bd61dd9d14
2014-08-13 10:39:27 +01:00

444 lines
13 KiB
C#

//
// MoonlightChannelBaseExtension.cs
//
// Author:
// Atsushi Enomoto <atsushi@ximian.com>
//
// Copyright (C) 2009 Novell, Inc. http://www.novell.com
//
// 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.CodeDom.Compiler;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.IO;
using System.Linq;
using System.Reflection;
using System.ServiceModel;
using System.ServiceModel.Channels;
using System.ServiceModel.Description;
using System.ServiceModel.Dispatcher;
using System.Threading;
namespace Mono.ServiceContractTool
{
class MoonlightChannelBaseContext
{
public MoonlightChannelBaseContractExtension Contract;
public List<MoonlightChannelBaseOperationExtension> Operations = new List<MoonlightChannelBaseOperationExtension> ();
public CodeTypeDeclaration ClientType { get; set; }
public CodeTypeDeclaration ChannelType { get; set; }
public void FindClientType (ServiceContractGenerationContext context)
{
var cd = context.Contract;
string name = cd.Name + "Client";
if (name [0] == 'I')
name = name.Substring (1);
foreach (CodeNamespace cns in context.ServiceContractGenerator.TargetCompileUnit.Namespaces)
foreach (CodeTypeDeclaration ct in cns.Types)
if (ct == context.ContractType)
foreach (CodeTypeDeclaration ct2 in cns.Types)
if (ct2.Name == name) {
ClientType = ct2;
return;
}
throw new Exception (String.Format ("Contract '{0}' not found", name));
}
public void Fixup ()
{
Contract.Fixup ();
foreach (var op in Operations)
op.Fixup ();
}
}
class MoonlightChannelBaseContractExtension : IContractBehavior, IServiceContractGenerationExtension
{
public MoonlightChannelBaseContractExtension (MoonlightChannelBaseContext mlContext, bool generateSync)
{
ml_context = mlContext;
generate_sync = generateSync;
}
MoonlightChannelBaseContext ml_context;
bool generate_sync;
// IContractBehavior
public void AddBindingParameters (ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
{
throw new NotSupportedException ();
}
public void ApplyClientBehavior (
ContractDescription description,
ServiceEndpoint endpoint,
ClientRuntime proxy)
{
throw new NotSupportedException ();
}
public void ApplyDispatchBehavior (
ContractDescription description,
ServiceEndpoint endpoint,
DispatchRuntime dispatch)
{
throw new NotSupportedException ();
}
public void Validate (
ContractDescription description,
ServiceEndpoint endpoint)
{
throw new NotSupportedException ();
}
// IServiceContractGenerationExtensions
public void GenerateContract (
ServiceContractGenerationContext context)
{
this.context = context;
ml_context.Contract = this;
}
ServiceContractGenerationContext context;
public void Fixup ()
{
ContractDescription cd = context.Contract;
ml_context.FindClientType (context);
var parentClass = ml_context.ClientType;
if (!generate_sync)
EliminateSync ();
string name = cd.Name + "Channel";
if (name [0] == 'I')
name = name.Substring (1);
var gt = new CodeTypeReference (cd.Name);
var clientBaseType = new CodeTypeReference ("System.ServiceModel.ClientBase", gt);
// this omits namespace, but should compile
var channelBase = new CodeTypeReference ("ChannelBase", gt);
var type = new CodeTypeDeclaration (name);
parentClass.Members.Add (type);
type.BaseTypes.Add (channelBase);
type.BaseTypes.Add (new CodeTypeReference (cd.Name));
type.TypeAttributes |= TypeAttributes.NestedPrivate;
ml_context.ChannelType = type;
// .ctor(ClientBase<T> client)
var ctor = new CodeConstructor ();
ctor.Attributes = MemberAttributes.Public;
ctor.Parameters.Add (
new CodeParameterDeclarationExpression (
clientBaseType, "client"));
ctor.BaseConstructorArgs.Add (
new CodeArgumentReferenceExpression ("client"));
type.Members.Add (ctor);
// In Client type:
// protected override TChannel CreateChannel()
var creator = new CodeMemberMethod ();
creator.Name = "CreateChannel";
creator.Attributes = MemberAttributes.Family | MemberAttributes.Override;
creator.ReturnType = gt;
creator.Statements.Add (
new CodeMethodReturnStatement (
new CodeCastExpression (
gt,
new CodeObjectCreateExpression (
new CodeTypeReference (name),
new CodeThisReferenceExpression ()))));
parentClass.Members.Add (creator);
// clear IExtensibleDataObject. Since there is *no* way
// to identify the type of a TypeReference, I cannot do
// anything but this brutal removal.
// Also clear ExtensionDataObject members.
foreach (CodeNamespace cns in context.ServiceContractGenerator.TargetCompileUnit.Namespaces) {
foreach (CodeTypeDeclaration ct in cns.Types) {
if (!ShouldPreserveBaseTypes (ct))
ct.BaseTypes.Clear ();
CodeTypeMember cp = null, cf = null;
foreach (CodeTypeMember cm in ct.Members) {
if (cm is CodeMemberProperty && cm.Name == "ExtensionData")
cp = cm;
else if (cm is CodeMemberField && cm.Name == "extensionDataField")
cf = cm;
}
if (cf != null)
ct.Members.Remove (cf);
if (cp != null)
ct.Members.Remove (cp);
}
}
}
bool ShouldPreserveBaseTypes (CodeTypeDeclaration ct)
{
foreach (CodeTypeReference cr in ct.BaseTypes) {
if (cr.BaseType == "System.ServiceModel.ClientBase`1")
return true;
if (cr.BaseType == "System.ComponentModel.AsyncCompletedEventArgs")
return true;
}
return false;
}
void EliminateSync ()
{
var type = context.ContractType;
// remove such OperationContract methods that do not have AsyncPattern parameter. It is sort of hack as it does not check the value (it might be "false").
var l = new List<CodeMemberMethod> ();
foreach (CodeMemberMethod cm in type.Members) {
bool isOperation = false, isAsync = false;
foreach (CodeAttributeDeclaration att in cm.CustomAttributes) {
if (att.Name == "System.ServiceModel.OperationContractAttribute")
isOperation = true;
else
continue;
foreach (CodeAttributeArgument aa in att.Arguments) {
if (aa.Name == "AsyncPattern") {
isAsync = true;
break;
}
}
if (isAsync)
break;
}
if (isOperation && !isAsync)
l.Add (cm);
}
foreach (var cm in l)
type.Members.Remove (cm);
// remove corresponding client implementation methods.
// It is sort of hack as it only checks method and
// parameter names (ideally we want to check parameter
// types, but there is no way to compare
// CodeTypeReferences).
var lc = new List<CodeMemberMethod> ();
foreach (var cm_ in ml_context.ClientType.Members) {
var cm = cm_ as CodeMemberMethod;
if (cm == null)
continue;
foreach (var sm in l) {
if (cm.Name != sm.Name || cm.Parameters.Count != sm.Parameters.Count)
continue;
bool diff = false;
for (int i = 0; i < cm.Parameters.Count; i++) {
var cp = cm.Parameters [i];
var sp = sm.Parameters [i];
if (cp.Direction != sp.Direction || cp.Name != sp.Name) {
diff = true;
break;
}
}
if (diff)
continue;
lc.Add (cm);
break;
}
}
foreach (var cm in lc)
ml_context.ClientType.Members.Remove (cm);
}
}
class MoonlightChannelBaseOperationExtension : IOperationBehavior, IOperationContractGenerationExtension
{
public MoonlightChannelBaseOperationExtension (MoonlightChannelBaseContext mlContext, bool generateSync)
{
ml_context = mlContext;
generate_sync = generateSync;
}
MoonlightChannelBaseContext ml_context;
bool generate_sync;
// IOperationBehavior
public void AddBindingParameters (
OperationDescription description,
BindingParameterCollection parameters)
{
throw new NotSupportedException ();
}
public void ApplyDispatchBehavior (
OperationDescription description,
DispatchOperation dispatch)
{
throw new NotSupportedException ();
}
public void ApplyClientBehavior (
OperationDescription description,
ClientOperation proxy)
{
throw new NotSupportedException ();
}
public void Validate (
OperationDescription description)
{
throw new NotSupportedException ();
}
// IOperationContractGenerationContext
public void GenerateOperation (OperationContractGenerationContext context)
{
this.context = context;
ml_context.Operations.Add (this);
}
OperationContractGenerationContext context;
public void Fixup ()
{
if (generate_sync)
FixupSync ();
FixupAsync ();
}
void FixupSync ()
{
var type = ml_context.ChannelType;
var od = context.Operation;
// sync method implementation
CodeMemberMethod cm = new CodeMemberMethod ();
type.Members.Add (cm);
cm.Name = od.Name;
cm.Attributes = MemberAttributes.Public
| MemberAttributes.Final;
var inArgs = new List<CodeParameterDeclarationExpression > ();
var outArgs = new List<CodeParameterDeclarationExpression > ();
foreach (CodeParameterDeclarationExpression p in context.SyncMethod.Parameters) {
inArgs.Add (p);
cm.Parameters.Add (p);
}
cm.ReturnType = context.SyncMethod.ReturnType;
var argsDecl = new CodeVariableDeclarationStatement (
typeof (object []),
"args",
new CodeArrayCreateExpression (typeof (object), inArgs.ConvertAll<CodeExpression> (decl => new CodeArgumentReferenceExpression (decl.Name)).ToArray ()));
cm.Statements.Add (argsDecl);
var args = new List<CodeExpression> ();
args.Add (new CodePrimitiveExpression (od.Name));
args.Add (new CodeVariableReferenceExpression ("args"));
CodeExpression call = new CodeMethodInvokeExpression (
new CodeBaseReferenceExpression (),
"Invoke",
args.ToArray ());
if (cm.ReturnType.BaseType == "System.Void")
cm.Statements.Add (new CodeExpressionStatement (call));
else
cm.Statements.Add (new CodeMethodReturnStatement (new CodeCastExpression (context.SyncMethod.ReturnType, call)));
}
public void FixupAsync ()
{
var type = ml_context.ChannelType;
var od = context.Operation;
var baseExpr = new CodeBaseReferenceExpression ();
var asyncResultType = new CodeTypeReference (typeof (IAsyncResult));
// BeginXxx() implementation
CodeMemberMethod cm = new CodeMemberMethod () {
Name = "Begin" + od.Name,
Attributes = MemberAttributes.Public | MemberAttributes.Final,
ReturnType = asyncResultType
};
type.Members.Add (cm);
var inArgs = new List<CodeParameterDeclarationExpression > ();
foreach (CodeParameterDeclarationExpression p in context.BeginMethod.Parameters) {
inArgs.Add (p);
cm.Parameters.Add (p);
}
inArgs.RemoveAt (inArgs.Count - 1);
inArgs.RemoveAt (inArgs.Count - 1);
var call = new CodeMethodInvokeExpression (
baseExpr,
"BeginInvoke",
new CodePrimitiveExpression (od.Name),
new CodeArrayCreateExpression (typeof (object), inArgs.ConvertAll<CodeExpression> (decl => new CodeArgumentReferenceExpression (decl.Name)).ToArray ()),
new CodeArgumentReferenceExpression ("asyncCallback"),
new CodeArgumentReferenceExpression ("userState"));
cm.Statements.Add (new CodeMethodReturnStatement (call));
// EndXxx() implementation
cm = new CodeMemberMethod () {
Name = "End" + od.Name,
Attributes = MemberAttributes.Public | MemberAttributes.Final,
ReturnType = context.EndMethod.ReturnType };
type.Members.Add (cm);
AddMethodParam (cm, typeof (IAsyncResult), "result");
var outArgs = new List<CodeParameterDeclarationExpression > ();
string resultArgName = "result";
var argsDecl = new CodeVariableDeclarationStatement (
typeof (object []),
"args",
new CodeArrayCreateExpression (typeof (object), new CodePrimitiveExpression (outArgs.Count)));
cm.Statements.Add (argsDecl);
var ret = new CodeMethodInvokeExpression (
baseExpr,
"EndInvoke",
new CodePrimitiveExpression (od.Name),
new CodeVariableReferenceExpression ("args"),
new CodeArgumentReferenceExpression (resultArgName));
if (cm.ReturnType.BaseType == "System.Void")
cm.Statements.Add (new CodeExpressionStatement (ret));
else
cm.Statements.Add (new CodeMethodReturnStatement (new CodeCastExpression (context.EndMethod.ReturnType, ret)));
}
void AddMethodParam (CodeMemberMethod cm, Type type, string name)
{
cm.Parameters.Add (new CodeParameterDeclarationExpression (new CodeTypeReference (type), name));
}
}
}