adjust version
[mcs.git] / tools / svcutil / MoonlightChannelBaseExtension.cs
blob404258efba945782e0431f8377725c95c9e68ee7
1 //
2 // MoonlightChannelBaseExtension.cs
3 //
4 // Author:
5 // Atsushi Enomoto <atsushi@ximian.com>
6 //
7 // Copyright (C) 2009 Novell, Inc. http://www.novell.com
8 //
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:
16 //
17 // The above copyright notice and this permission notice shall be
18 // included in all copies or substantial portions of the Software.
19 //
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.
28 using System;
29 using System.CodeDom;
30 using System.CodeDom.Compiler;
31 using System.Collections.Generic;
32 using System.Collections.ObjectModel;
33 using System.IO;
34 using System.Linq;
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";
56 if (name [0] == 'I')
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) {
64 ClientType = ct2;
65 return;
67 throw new Exception (String.Format ("Contract '{0}' not found", name));
70 public void Fixup ()
72 Contract.Fixup ();
73 foreach (var op in Operations)
74 op.Fixup ();
78 class MoonlightChannelBaseContractExtension : IContractBehavior, IServiceContractGenerationExtension
80 public MoonlightChannelBaseContractExtension (MoonlightChannelBaseContext mlContext, bool generateSync)
82 ml_context = mlContext;
83 generate_sync = generateSync;
86 MoonlightChannelBaseContext ml_context;
87 bool generate_sync;
89 // IContractBehavior
90 public void AddBindingParameters (ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters)
92 throw new NotSupportedException ();
95 public void ApplyClientBehavior (
96 ContractDescription description,
97 ServiceEndpoint endpoint,
98 ClientRuntime proxy)
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;
129 public void Fixup ()
131 ContractDescription cd = context.Contract;
132 ml_context.FindClientType (context);
133 var parentClass = ml_context.ClientType;
135 if (!generate_sync)
136 EliminateSync ();
138 string name = cd.Name + "Channel";
139 if (name [0] == 'I')
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);
164 // In Client type:
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 foreach (CodeNamespace cns in context.ServiceContractGenerator.TargetCompileUnit.Namespaces)
183 foreach (CodeTypeDeclaration ct in cns.Types)
184 if (ct != ml_context.ClientType && !ct.Name.EndsWith ("EventArgs", StringComparison.Ordinal))
185 ct.BaseTypes.Clear ();
188 void EliminateSync ()
190 var type = context.ContractType;
192 // 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").
193 var l = new List<CodeMemberMethod> ();
194 foreach (CodeMemberMethod cm in type.Members) {
195 bool isOperation = false, isAsync = false;
196 foreach (CodeAttributeDeclaration att in cm.CustomAttributes) {
197 if (att.Name == "System.ServiceModel.OperationContractAttribute")
198 isOperation = true;
199 else
200 continue;
201 foreach (CodeAttributeArgument aa in att.Arguments) {
202 if (aa.Name == "AsyncPattern") {
203 isAsync = true;
204 break;
207 if (isAsync)
208 break;
210 if (isOperation && !isAsync)
211 l.Add (cm);
213 foreach (var cm in l)
214 type.Members.Remove (cm);
216 // remove corresponding client implementation methods.
217 // It is sort of hack as it only checks method and
218 // parameter names (ideally we want to check parameter
219 // types, but there is no way to compare
220 // CodeTypeReferences).
221 var lc = new List<CodeMemberMethod> ();
222 foreach (var cm_ in ml_context.ClientType.Members) {
223 var cm = cm_ as CodeMemberMethod;
224 if (cm == null)
225 continue;
226 foreach (var sm in l) {
227 if (cm.Name != sm.Name || cm.Parameters.Count != sm.Parameters.Count)
228 continue;
229 bool diff = false;
230 for (int i = 0; i < cm.Parameters.Count; i++) {
231 var cp = cm.Parameters [i];
232 var sp = sm.Parameters [i];
233 if (cp.Direction != sp.Direction || cp.Name != sp.Name) {
234 diff = true;
235 break;
238 if (diff)
239 continue;
240 lc.Add (cm);
241 break;
244 foreach (var cm in lc)
245 ml_context.ClientType.Members.Remove (cm);
249 class MoonlightChannelBaseOperationExtension : IOperationBehavior, IOperationContractGenerationExtension
251 public MoonlightChannelBaseOperationExtension (MoonlightChannelBaseContext mlContext, bool generateSync)
253 ml_context = mlContext;
254 generate_sync = generateSync;
257 MoonlightChannelBaseContext ml_context;
258 bool generate_sync;
260 // IOperationBehavior
262 public void AddBindingParameters (
263 OperationDescription description,
264 BindingParameterCollection parameters)
266 throw new NotSupportedException ();
269 public void ApplyDispatchBehavior (
270 OperationDescription description,
271 DispatchOperation dispatch)
273 throw new NotSupportedException ();
276 public void ApplyClientBehavior (
277 OperationDescription description,
278 ClientOperation proxy)
280 throw new NotSupportedException ();
283 public void Validate (
284 OperationDescription description)
286 throw new NotSupportedException ();
289 // IOperationContractGenerationContext
291 public void GenerateOperation (OperationContractGenerationContext context)
293 this.context = context;
294 ml_context.Operations.Add (this);
297 OperationContractGenerationContext context;
299 public void Fixup ()
301 if (generate_sync)
302 FixupSync ();
303 FixupAsync ();
306 void FixupSync ()
308 var type = ml_context.ChannelType;
309 var od = context.Operation;
311 // sync method implementation
312 CodeMemberMethod cm = new CodeMemberMethod ();
313 type.Members.Add (cm);
314 cm.Name = od.Name;
315 cm.Attributes = MemberAttributes.Public
316 | MemberAttributes.Final;
318 var inArgs = new List<CodeParameterDeclarationExpression > ();
319 var outArgs = new List<CodeParameterDeclarationExpression > ();
321 foreach (CodeParameterDeclarationExpression p in context.SyncMethod.Parameters) {
322 inArgs.Add (p);
323 cm.Parameters.Add (p);
326 cm.ReturnType = context.SyncMethod.ReturnType;
328 var argsDecl = new CodeVariableDeclarationStatement (
329 typeof (object []),
330 "args",
331 new CodeArrayCreateExpression (typeof (object), inArgs.ConvertAll<CodeExpression> (decl => new CodeArgumentReferenceExpression (decl.Name)).ToArray ()));
332 cm.Statements.Add (argsDecl);
334 var args = new List<CodeExpression> ();
335 args.Add (new CodePrimitiveExpression (od.Name));
336 args.Add (new CodeVariableReferenceExpression ("args"));
338 CodeExpression call = new CodeMethodInvokeExpression (
339 new CodeBaseReferenceExpression (),
340 "Invoke",
341 args.ToArray ());
343 if (cm.ReturnType.BaseType == "System.Void")
344 cm.Statements.Add (new CodeExpressionStatement (call));
345 else
346 cm.Statements.Add (new CodeMethodReturnStatement (new CodeCastExpression (context.SyncMethod.ReturnType, call)));
349 public void FixupAsync ()
351 var type = ml_context.ChannelType;
352 var od = context.Operation;
354 var baseExpr = new CodeBaseReferenceExpression ();
355 var asyncResultType = new CodeTypeReference (typeof (IAsyncResult));
357 // BeginXxx() implementation
358 CodeMemberMethod cm = new CodeMemberMethod () {
359 Name = "Begin" + od.Name,
360 Attributes = MemberAttributes.Public | MemberAttributes.Final,
361 ReturnType = asyncResultType
363 type.Members.Add (cm);
365 var inArgs = new List<CodeParameterDeclarationExpression > ();
366 foreach (CodeParameterDeclarationExpression p in context.BeginMethod.Parameters) {
367 inArgs.Add (p);
368 cm.Parameters.Add (p);
370 inArgs.RemoveAt (inArgs.Count - 1);
371 inArgs.RemoveAt (inArgs.Count - 1);
373 var call = new CodeMethodInvokeExpression (
374 baseExpr,
375 "BeginInvoke",
376 new CodePrimitiveExpression (od.Name),
377 new CodeArrayCreateExpression (typeof (object), inArgs.ConvertAll<CodeExpression> (decl => new CodeArgumentReferenceExpression (decl.Name)).ToArray ()),
378 new CodeArgumentReferenceExpression ("asyncCallback"),
379 new CodeArgumentReferenceExpression ("userState"));
380 cm.Statements.Add (new CodeMethodReturnStatement (call));
382 // EndXxx() implementation
384 cm = new CodeMemberMethod () {
385 Name = "End" + od.Name,
386 Attributes = MemberAttributes.Public | MemberAttributes.Final,
387 ReturnType = context.EndMethod.ReturnType };
388 type.Members.Add (cm);
390 AddMethodParam (cm, typeof (IAsyncResult), "result");
392 var outArgs = new List<CodeParameterDeclarationExpression > ();
394 string resultArgName = "result";
395 var argsDecl = new CodeVariableDeclarationStatement (
396 typeof (object []),
397 "args",
398 new CodeArrayCreateExpression (typeof (object), new CodePrimitiveExpression (outArgs.Count)));
399 cm.Statements.Add (argsDecl);
401 var cast = new CodeCastExpression (
402 context.EndMethod.ReturnType,
403 new CodeMethodInvokeExpression (
404 baseExpr,
405 "EndInvoke",
406 new CodePrimitiveExpression (od.Name),
407 new CodeVariableReferenceExpression ("args"),
408 new CodeArgumentReferenceExpression (resultArgName)));
410 if (cm.ReturnType.BaseType == "System.Void")
411 cm.Statements.Add (new CodeExpressionStatement (cast));
412 else
413 cm.Statements.Add (new CodeMethodReturnStatement (cast));
416 void AddMethodParam (CodeMemberMethod cm, Type type, string name)
418 cm.Parameters.Add (new CodeParameterDeclarationExpression (new CodeTypeReference (type), name));