2 // MoonlightChannelBaseExtension.cs
5 // Atsushi Enomoto <atsushi@ximian.com>
7 // Copyright (C) 2009 Novell, Inc. http://www.novell.com
9 // Permission is hereby granted, free of charge, to any person obtaining
10 // a copy of this software and associated documentation files (the
11 // "Software"), to deal in the Software without restriction, including
12 // without limitation the rights to use, copy, modify, merge, publish,
13 // distribute, sublicense, and/or sell copies of the Software, and to
14 // permit persons to whom the Software is furnished to do so, subject to
15 // the following conditions:
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
20 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
22 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
23 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
24 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
25 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
26 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
30 using System.CodeDom.Compiler;
31 using System.Collections.Generic;
32 using System.Collections.ObjectModel;
35 using System.Reflection;
36 using System.ServiceModel;
37 using System.ServiceModel.Channels;
38 using System.ServiceModel.Description;
39 using System.ServiceModel.Dispatcher;
40 using System.Threading;
42 namespace Mono.ServiceContractTool
44 class MoonlightChannelBaseContext
46 public MoonlightChannelBaseContractExtension Contract;
47 public List<MoonlightChannelBaseOperationExtension> Operations = new List<MoonlightChannelBaseOperationExtension> ();
49 public CodeTypeDeclaration ClientType { get; set; }
50 public CodeTypeDeclaration ChannelType { get; set; }
52 public void FindClientType (ServiceContractGenerationContext context)
54 var cd = context.Contract;
55 string name = cd.Name + "Client";
57 name = name.Substring (1);
59 foreach (CodeNamespace cns in context.ServiceContractGenerator.TargetCompileUnit.Namespaces)
60 foreach (CodeTypeDeclaration ct in cns.Types)
61 if (ct == context.ContractType)
62 foreach (CodeTypeDeclaration ct2 in cns.Types)
63 if (ct2.Name == name) {
67 throw new Exception (String.Format ("Contract '{0}' not found", name));
73 foreach (var op in Operations)
78 class MoonlightChannelBaseContractExtension : IContractBehavior, IServiceContractGenerationExtension
80 public MoonlightChannelBaseContractExtension (MoonlightChannelBaseContext mlContext, bool generateSync)
82 ml_context = mlContext;
83 generate_sync = generateSync;
86 MoonlightChannelBaseContext ml_context;
90 public void AddBindingParameters (ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
92 throw new NotSupportedException ();
95 public void ApplyClientBehavior (
96 ContractDescription description,
97 ServiceEndpoint endpoint,
100 throw new NotSupportedException ();
103 public void ApplyDispatchBehavior (
104 ContractDescription description,
105 ServiceEndpoint endpoint,
106 DispatchRuntime dispatch)
108 throw new NotSupportedException ();
111 public void Validate (
112 ContractDescription description,
113 ServiceEndpoint endpoint)
115 throw new NotSupportedException ();
118 // IServiceContractGenerationExtensions
120 public void GenerateContract (
121 ServiceContractGenerationContext context)
123 this.context = context;
124 ml_context.Contract = this;
127 ServiceContractGenerationContext context;
131 ContractDescription cd = context.Contract;
132 ml_context.FindClientType (context);
133 var parentClass = ml_context.ClientType;
138 string name = cd.Name + "Channel";
140 name = name.Substring (1);
142 var gt = new CodeTypeReference (cd.Name);
143 var clientBaseType = new CodeTypeReference ("System.ServiceModel.ClientBase", gt);
144 // this omits namespace, but should compile
145 var channelBase = new CodeTypeReference ("ChannelBase", gt);
146 var type = new CodeTypeDeclaration (name);
147 parentClass.Members.Add (type);
148 type.BaseTypes.Add (channelBase);
149 type.BaseTypes.Add (new CodeTypeReference (cd.Name));
150 type.TypeAttributes |= TypeAttributes.NestedPrivate;
152 ml_context.ChannelType = type;
154 // .ctor(ClientBase<T> client)
155 var ctor = new CodeConstructor ();
156 ctor.Attributes = MemberAttributes.Public;
157 ctor.Parameters.Add (
158 new CodeParameterDeclarationExpression (
159 clientBaseType, "client"));
160 ctor.BaseConstructorArgs.Add (
161 new CodeArgumentReferenceExpression ("client"));
162 type.Members.Add (ctor);
165 // protected override TChannel CreateChannel()
166 var creator = new CodeMemberMethod ();
167 creator.Name = "CreateChannel";
168 creator.Attributes = MemberAttributes.Family | MemberAttributes.Override;
169 creator.ReturnType = gt;
170 creator.Statements.Add (
171 new CodeMethodReturnStatement (
172 new CodeCastExpression (
174 new CodeObjectCreateExpression (
175 new CodeTypeReference (name),
176 new CodeThisReferenceExpression ()))));
177 parentClass.Members.Add (creator);
179 // clear IExtensibleDataObject. Since there is *no* way
180 // to identify the type of a TypeReference, I cannot do
181 // anything but this brutal removal.
182 // Also clear ExtensionDataObject members.
183 foreach (CodeNamespace cns in context.ServiceContractGenerator.TargetCompileUnit.Namespaces) {
184 foreach (CodeTypeDeclaration ct in cns.Types) {
185 if (!ShouldPreserveBaseTypes (ct))
186 ct.BaseTypes.Clear ();
187 CodeTypeMember cp = null, cf = null;
188 foreach (CodeTypeMember cm in ct.Members) {
189 if (cm is CodeMemberProperty && cm.Name == "ExtensionData")
191 else if (cm is CodeMemberField && cm.Name == "extensionDataField")
195 ct.Members.Remove (cf);
197 ct.Members.Remove (cp);
202 bool ShouldPreserveBaseTypes (CodeTypeDeclaration ct)
204 foreach (CodeTypeReference cr in ct.BaseTypes) {
205 if (cr.BaseType == "System.ServiceModel.ClientBase`1")
207 if (cr.BaseType == "System.ComponentModel.AsyncCompletedEventArgs")
213 void EliminateSync ()
215 var type = context.ContractType;
217 // 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").
218 var l = new List<CodeMemberMethod> ();
219 foreach (CodeMemberMethod cm in type.Members) {
220 bool isOperation = false, isAsync = false;
221 foreach (CodeAttributeDeclaration att in cm.CustomAttributes) {
222 if (att.Name == "System.ServiceModel.OperationContractAttribute")
226 foreach (CodeAttributeArgument aa in att.Arguments) {
227 if (aa.Name == "AsyncPattern") {
235 if (isOperation && !isAsync)
238 foreach (var cm in l)
239 type.Members.Remove (cm);
241 // remove corresponding client implementation methods.
242 // It is sort of hack as it only checks method and
243 // parameter names (ideally we want to check parameter
244 // types, but there is no way to compare
245 // CodeTypeReferences).
246 var lc = new List<CodeMemberMethod> ();
247 foreach (var cm_ in ml_context.ClientType.Members) {
248 var cm = cm_ as CodeMemberMethod;
251 foreach (var sm in l) {
252 if (cm.Name != sm.Name || cm.Parameters.Count != sm.Parameters.Count)
255 for (int i = 0; i < cm.Parameters.Count; i++) {
256 var cp = cm.Parameters [i];
257 var sp = sm.Parameters [i];
258 if (cp.Direction != sp.Direction || cp.Name != sp.Name) {
269 foreach (var cm in lc)
270 ml_context.ClientType.Members.Remove (cm);
274 class MoonlightChannelBaseOperationExtension : IOperationBehavior, IOperationContractGenerationExtension
276 public MoonlightChannelBaseOperationExtension (MoonlightChannelBaseContext mlContext, bool generateSync)
278 ml_context = mlContext;
279 generate_sync = generateSync;
282 MoonlightChannelBaseContext ml_context;
285 // IOperationBehavior
287 public void AddBindingParameters (
288 OperationDescription description,
289 BindingParameterCollection parameters)
291 throw new NotSupportedException ();
294 public void ApplyDispatchBehavior (
295 OperationDescription description,
296 DispatchOperation dispatch)
298 throw new NotSupportedException ();
301 public void ApplyClientBehavior (
302 OperationDescription description,
303 ClientOperation proxy)
305 throw new NotSupportedException ();
308 public void Validate (
309 OperationDescription description)
311 throw new NotSupportedException ();
314 // IOperationContractGenerationContext
316 public void GenerateOperation (OperationContractGenerationContext context)
318 this.context = context;
319 ml_context.Operations.Add (this);
322 OperationContractGenerationContext context;
333 var type = ml_context.ChannelType;
334 var od = context.Operation;
336 // sync method implementation
337 CodeMemberMethod cm = new CodeMemberMethod ();
338 type.Members.Add (cm);
340 cm.Attributes = MemberAttributes.Public
341 | MemberAttributes.Final;
343 var inArgs = new List<CodeParameterDeclarationExpression > ();
344 var outArgs = new List<CodeParameterDeclarationExpression > ();
346 foreach (CodeParameterDeclarationExpression p in context.SyncMethod.Parameters) {
348 cm.Parameters.Add (p);
351 cm.ReturnType = context.SyncMethod.ReturnType;
353 var argsDecl = new CodeVariableDeclarationStatement (
356 new CodeArrayCreateExpression (typeof (object), inArgs.ConvertAll<CodeExpression> (decl => new CodeArgumentReferenceExpression (decl.Name)).ToArray ()));
357 cm.Statements.Add (argsDecl);
359 var args = new List<CodeExpression> ();
360 args.Add (new CodePrimitiveExpression (od.Name));
361 args.Add (new CodeVariableReferenceExpression ("args"));
363 CodeExpression call = new CodeMethodInvokeExpression (
364 new CodeBaseReferenceExpression (),
368 if (cm.ReturnType.BaseType == "System.Void")
369 cm.Statements.Add (new CodeExpressionStatement (call));
371 cm.Statements.Add (new CodeMethodReturnStatement (new CodeCastExpression (context.SyncMethod.ReturnType, call)));
374 public void FixupAsync ()
376 var type = ml_context.ChannelType;
377 var od = context.Operation;
379 var baseExpr = new CodeBaseReferenceExpression ();
380 var asyncResultType = new CodeTypeReference (typeof (IAsyncResult));
382 // BeginXxx() implementation
383 CodeMemberMethod cm = new CodeMemberMethod () {
384 Name = "Begin" + od.Name,
385 Attributes = MemberAttributes.Public | MemberAttributes.Final,
386 ReturnType = asyncResultType
388 type.Members.Add (cm);
390 var inArgs = new List<CodeParameterDeclarationExpression > ();
391 foreach (CodeParameterDeclarationExpression p in context.BeginMethod.Parameters) {
393 cm.Parameters.Add (p);
395 inArgs.RemoveAt (inArgs.Count - 1);
396 inArgs.RemoveAt (inArgs.Count - 1);
398 var call = new CodeMethodInvokeExpression (
401 new CodePrimitiveExpression (od.Name),
402 new CodeArrayCreateExpression (typeof (object), inArgs.ConvertAll<CodeExpression> (decl => new CodeArgumentReferenceExpression (decl.Name)).ToArray ()),
403 new CodeArgumentReferenceExpression ("asyncCallback"),
404 new CodeArgumentReferenceExpression ("userState"));
405 cm.Statements.Add (new CodeMethodReturnStatement (call));
407 // EndXxx() implementation
409 cm = new CodeMemberMethod () {
410 Name = "End" + od.Name,
411 Attributes = MemberAttributes.Public | MemberAttributes.Final,
412 ReturnType = context.EndMethod.ReturnType };
413 type.Members.Add (cm);
415 AddMethodParam (cm, typeof (IAsyncResult), "result");
417 var outArgs = new List<CodeParameterDeclarationExpression > ();
419 string resultArgName = "result";
420 var argsDecl = new CodeVariableDeclarationStatement (
423 new CodeArrayCreateExpression (typeof (object), new CodePrimitiveExpression (outArgs.Count)));
424 cm.Statements.Add (argsDecl);
426 var ret = new CodeMethodInvokeExpression (
429 new CodePrimitiveExpression (od.Name),
430 new CodeVariableReferenceExpression ("args"),
431 new CodeArgumentReferenceExpression (resultArgName));
432 if (cm.ReturnType.BaseType == "System.Void")
433 cm.Statements.Add (new CodeExpressionStatement (ret));
435 cm.Statements.Add (new CodeMethodReturnStatement (new CodeCastExpression (context.EndMethod.ReturnType, ret)));
438 void AddMethodParam (CodeMemberMethod cm, Type type, string name)
440 cm.Parameters.Add (new CodeParameterDeclarationExpression (new CodeTypeReference (type), name));