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 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")
201 foreach (CodeAttributeArgument aa
in att
.Arguments
) {
202 if (aa
.Name
== "AsyncPattern") {
210 if (isOperation
&& !isAsync
)
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
;
226 foreach (var sm
in l
) {
227 if (cm
.Name
!= sm
.Name
|| cm
.Parameters
.Count
!= sm
.Parameters
.Count
)
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
) {
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
;
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
;
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
);
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
) {
323 cm
.Parameters
.Add (p
);
326 cm
.ReturnType
= context
.SyncMethod
.ReturnType
;
328 var argsDecl
= new CodeVariableDeclarationStatement (
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 (),
343 if (cm
.ReturnType
.BaseType
== "System.Void")
344 cm
.Statements
.Add (new CodeExpressionStatement (call
));
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
) {
368 cm
.Parameters
.Add (p
);
370 inArgs
.RemoveAt (inArgs
.Count
- 1);
371 inArgs
.RemoveAt (inArgs
.Count
- 1);
373 var call
= new CodeMethodInvokeExpression (
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 (
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 (
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
));
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
));