ServiceContractGenerator.cs 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. //
  2. // ServiceContractGenerator.cs
  3. //
  4. // Author:
  5. // Atsushi Enomoto <[email protected]>
  6. //
  7. // Copyright (C) 2005 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.
  27. //
  28. using System;
  29. using System.CodeDom;
  30. using System.Collections.Generic;
  31. using System.Collections.ObjectModel;
  32. using System.Configuration;
  33. using System.Reflection;
  34. using System.Runtime.Serialization;
  35. using System.ServiceModel;
  36. using System.ServiceModel.Channels;
  37. using System.ServiceModel.Configuration;
  38. using System.Xml.Schema;
  39. using System.Xml.Serialization;
  40. using ConfigurationType = System.Configuration.Configuration;
  41. using QName = System.Xml.XmlQualifiedName;
  42. namespace System.ServiceModel.Description
  43. {
  44. public class ServiceContractGenerator
  45. {
  46. CodeCompileUnit ccu;
  47. ConfigurationType config;
  48. Collection<MetadataConversionError> errors
  49. = new Collection<MetadataConversionError> ();
  50. Dictionary<string,string> nsmappings
  51. = new Dictionary<string,string> ();
  52. Dictionary<ContractDescription,Type> referenced_types
  53. = new Dictionary<ContractDescription,Type> ();
  54. ServiceContractGenerationOptions options;
  55. Dictionary<QName, QName> imported_names = null;
  56. public ServiceContractGenerator ()
  57. : this (null, null)
  58. {
  59. }
  60. public ServiceContractGenerator (CodeCompileUnit ccu)
  61. : this (ccu, null)
  62. {
  63. }
  64. public ServiceContractGenerator (ConfigurationType config)
  65. : this (null, config)
  66. {
  67. }
  68. public ServiceContractGenerator (CodeCompileUnit ccu, ConfigurationType config)
  69. {
  70. if (ccu == null)
  71. this.ccu = new CodeCompileUnit ();
  72. else
  73. this.ccu = ccu;
  74. this.config = config;
  75. Options |= ServiceContractGenerationOptions.ChannelInterface |
  76. ServiceContractGenerationOptions.ClientClass;
  77. }
  78. public ConfigurationType Configuration {
  79. get { return config; }
  80. }
  81. public Collection<MetadataConversionError> Errors {
  82. get { return errors; }
  83. }
  84. public Dictionary<string,string> NamespaceMappings {
  85. get { return nsmappings; }
  86. }
  87. public ServiceContractGenerationOptions Options {
  88. get { return options; }
  89. set { options = value; }
  90. }
  91. public Dictionary<ContractDescription,Type> ReferencedTypes {
  92. get { return referenced_types; }
  93. }
  94. public CodeCompileUnit TargetCompileUnit {
  95. get { return ccu; }
  96. }
  97. [MonoTODO]
  98. public void GenerateBinding (Binding binding,
  99. out string bindingSectionName,
  100. out string configurationName)
  101. {
  102. throw new NotImplementedException ();
  103. }
  104. #region Service Contract Type
  105. // Those implementation classes are very likely to be split
  106. // into different classes.
  107. [MonoTODO]
  108. public CodeTypeReference GenerateServiceContractType (
  109. ContractDescription contractDescription)
  110. {
  111. CodeNamespace cns = GetNamespace (contractDescription.Namespace);
  112. imported_names = new Dictionary<QName, QName> ();
  113. try {
  114. return ExportInterface (contractDescription, cns);
  115. } finally {
  116. if ((Options & ServiceContractGenerationOptions.ClientClass) != 0)
  117. GenerateProxyClass (contractDescription, cns);
  118. if ((Options & ServiceContractGenerationOptions.ChannelInterface) != 0)
  119. GenerateChannelInterface (contractDescription, cns);
  120. }
  121. }
  122. CodeNamespace GetNamespace (string ns)
  123. {
  124. if (ns == null)
  125. ns = String.Empty;
  126. foreach (CodeNamespace cns in ccu.Namespaces)
  127. if (cns.Name == ns)
  128. return cns;
  129. CodeNamespace ncns = new CodeNamespace ();
  130. //ncns.Name = ns;
  131. ccu.Namespaces.Add (ncns);
  132. return ncns;
  133. }
  134. CodeTypeDeclaration GetTypeDeclaration (CodeNamespace cns, string name)
  135. {
  136. foreach (CodeTypeDeclaration type in cns.Types)
  137. if (type.Name == name)
  138. return type;
  139. return null;
  140. }
  141. void GenerateProxyClass (ContractDescription cd, CodeNamespace cns)
  142. {
  143. string name = cd.Name + "Client";
  144. if (name [0] == 'I')
  145. name = name.Substring (1);
  146. CodeTypeDeclaration type = GetTypeDeclaration (cns, name);
  147. if (type != null)
  148. return; // already imported
  149. CodeTypeReference clientBase = new CodeTypeReference (typeof (ClientBase<int>).GetGenericTypeDefinition ());
  150. clientBase.TypeArguments.Add (new CodeTypeReference (cd.Name));
  151. type = new CodeTypeDeclaration (name);
  152. cns.Types.Add (type);
  153. type.TypeAttributes = TypeAttributes.Public;
  154. type.BaseTypes.Add (clientBase);
  155. type.BaseTypes.Add (new CodeTypeReference (cd.Name));
  156. // .ctor()
  157. CodeConstructor ctor = new CodeConstructor ();
  158. ctor.Attributes = MemberAttributes.Public;
  159. type.Members.Add (ctor);
  160. // .ctor(string endpointConfigurationName)
  161. ctor = new CodeConstructor ();
  162. ctor.Attributes = MemberAttributes.Public;
  163. ctor.Parameters.Add (
  164. new CodeParameterDeclarationExpression (
  165. new CodeTypeReference (typeof (string)), "endpointConfigurationName"));
  166. ctor.BaseConstructorArgs.Add (
  167. new CodeArgumentReferenceExpression ("endpointConfigurationName"));
  168. type.Members.Add (ctor);
  169. // .ctor(string endpointConfigurationName, string remoteAddress)
  170. ctor = new CodeConstructor ();
  171. ctor.Attributes = MemberAttributes.Public;
  172. ctor.Parameters.Add (
  173. new CodeParameterDeclarationExpression (
  174. new CodeTypeReference (typeof (string)), "endpointConfigurationName"));
  175. ctor.Parameters.Add (
  176. new CodeParameterDeclarationExpression (
  177. new CodeTypeReference (typeof (string)), "remoteAddress"));
  178. ctor.BaseConstructorArgs.Add (
  179. new CodeArgumentReferenceExpression ("endpointConfigurationName"));
  180. ctor.BaseConstructorArgs.Add (
  181. new CodeArgumentReferenceExpression ("remoteAddress"));
  182. type.Members.Add (ctor);
  183. // .ctor(string endpointConfigurationName, EndpointAddress remoteAddress)
  184. ctor = new CodeConstructor ();
  185. ctor.Attributes = MemberAttributes.Public;
  186. ctor.Parameters.Add (
  187. new CodeParameterDeclarationExpression (
  188. new CodeTypeReference (typeof (string)), "endpointConfigurationName"));
  189. ctor.Parameters.Add (
  190. new CodeParameterDeclarationExpression (
  191. new CodeTypeReference (typeof (EndpointAddress)), "remoteAddress"));
  192. ctor.BaseConstructorArgs.Add (
  193. new CodeArgumentReferenceExpression ("endpointConfigurationName"));
  194. ctor.BaseConstructorArgs.Add (
  195. new CodeArgumentReferenceExpression ("remoteAddress"));
  196. type.Members.Add (ctor);
  197. // .ctor(Binding,EndpointAddress)
  198. ctor = new CodeConstructor ();
  199. ctor.Attributes = MemberAttributes.Public;
  200. ctor.Parameters.Add (
  201. new CodeParameterDeclarationExpression (
  202. new CodeTypeReference (typeof (Binding)), "binding"));
  203. ctor.Parameters.Add (
  204. new CodeParameterDeclarationExpression (
  205. new CodeTypeReference (typeof (EndpointAddress)), "endpoint"));
  206. ctor.BaseConstructorArgs.Add (
  207. new CodeArgumentReferenceExpression ("binding"));
  208. ctor.BaseConstructorArgs.Add (
  209. new CodeArgumentReferenceExpression ("endpoint"));
  210. type.Members.Add (ctor);
  211. // service contract methods
  212. AddImplementationMethods (type, cd);
  213. }
  214. void GenerateChannelInterface (ContractDescription cd, CodeNamespace cns)
  215. {
  216. string name = cd.Name + "Channel";
  217. CodeTypeDeclaration type = GetTypeDeclaration (cns, name);
  218. if (type != null)
  219. return;
  220. type = new CodeTypeDeclaration ();
  221. type.Name = name;
  222. type.TypeAttributes = TypeAttributes.Interface | TypeAttributes.Public;
  223. cns.Types.Add (type);
  224. type.BaseTypes.Add (ExportInterface (cd, cns));
  225. type.BaseTypes.Add (new CodeTypeReference (typeof (System.ServiceModel.IClientChannel)));
  226. }
  227. CodeTypeReference ExportInterface (ContractDescription cd, CodeNamespace cns)
  228. {
  229. CodeTypeDeclaration type = GetTypeDeclaration (cns, cd.Name);
  230. if (type != null)
  231. return new CodeTypeReference (type.Name);
  232. type = new CodeTypeDeclaration ();
  233. type.TypeAttributes = TypeAttributes.Interface;
  234. type.TypeAttributes |= TypeAttributes.Public;
  235. cns.Types.Add (type);
  236. type.Name = cd.Name;
  237. CodeAttributeDeclaration ad =
  238. new CodeAttributeDeclaration (
  239. new CodeTypeReference (
  240. typeof (ServiceContractAttribute)));
  241. ad.Arguments.Add (new CodeAttributeArgument ("Namespace", new CodePrimitiveExpression (cd.Namespace)));
  242. type.CustomAttributes.Add (ad);
  243. AddOperationMethods (type, cd);
  244. return new CodeTypeReference (type.Name);
  245. }
  246. void AddOperationMethods (CodeTypeDeclaration type, ContractDescription cd)
  247. {
  248. foreach (OperationDescription od in cd.Operations) {
  249. CodeMemberMethod cm = new CodeMemberMethod ();
  250. type.Members.Add (cm);
  251. cm.Name = od.Name;
  252. if (od.SyncMethod != null) {
  253. ExportParameters (cm, od.SyncMethod.GetParameters ());
  254. cm.ReturnType = new CodeTypeReference (od.SyncMethod.ReturnType);
  255. } else {
  256. ExportMessages (od.Messages, cm, false);
  257. }
  258. // [OperationContract (Action = "...", ReplyAction = "..")]
  259. CodeAttributeDeclaration ad =
  260. new CodeAttributeDeclaration (
  261. new CodeTypeReference (
  262. typeof (OperationContractAttribute)));
  263. foreach (MessageDescription md in od.Messages) {
  264. if (md.Direction == MessageDirection.Input)
  265. ad.Arguments.Add (new CodeAttributeArgument ("Action", new CodePrimitiveExpression (md.Action)));
  266. else
  267. ad.Arguments.Add (new CodeAttributeArgument ("ReplyAction", new CodePrimitiveExpression (md.Action)));
  268. }
  269. cm.CustomAttributes.Add (ad);
  270. }
  271. }
  272. void ExportParameters (CodeMemberMethod method, ParameterInfo [] parameters)
  273. {
  274. foreach (ParameterInfo pi in parameters)
  275. method.Parameters.Add (
  276. new CodeParameterDeclarationExpression (
  277. new CodeTypeReference (pi.ParameterType),
  278. pi.Name));
  279. }
  280. void AddImplementationMethods (CodeTypeDeclaration type, ContractDescription cd)
  281. {
  282. foreach (OperationDescription od in cd.Operations) {
  283. CodeMemberMethod cm = new CodeMemberMethod ();
  284. type.Members.Add (cm);
  285. cm.Name = od.Name;
  286. cm.Attributes = MemberAttributes.Public
  287. | MemberAttributes.Final;
  288. CodeExpression [] args = null;
  289. if (od.SyncMethod != null) {
  290. ParameterInfo [] pars = od.SyncMethod.GetParameters ();
  291. ExportParameters (cm, pars);
  292. cm.ReturnType = new CodeTypeReference (od.SyncMethod.ReturnType);
  293. args = new CodeExpression [pars.Length];
  294. int i = 0;
  295. foreach (ParameterInfo pi in pars)
  296. args [i ++] = new CodeArgumentReferenceExpression (pi.Name);
  297. } else {
  298. args = ExportMessages (od.Messages, cm, true);
  299. }
  300. CodeExpression call = new CodeMethodInvokeExpression (
  301. new CodePropertyReferenceExpression (
  302. new CodeBaseReferenceExpression (),
  303. "Channel"),
  304. cm.Name,
  305. args);
  306. if (cm.ReturnType.BaseType == "System.Void")
  307. cm.Statements.Add (new CodeExpressionStatement (call));
  308. else
  309. cm.Statements.Add (new CodeMethodReturnStatement (call));
  310. }
  311. }
  312. private CodeExpression[] ExportMessages (MessageDescriptionCollection messages, CodeMemberMethod method, bool return_args)
  313. {
  314. CodeExpression [] args = null;
  315. foreach (MessageDescription md in messages) {
  316. if (md.Direction == MessageDirection.Output) {
  317. if (md.Body.ReturnValue != null) {
  318. ExportDataContract (md.Body.ReturnValue.XmlTypeMapping);
  319. method.ReturnType = new CodeTypeReference (md.Body.ReturnValue.TypeName.Name);
  320. }
  321. continue;
  322. }
  323. if (return_args)
  324. args = new CodeExpression [md.Body.Parts.Count];
  325. MessagePartDescriptionCollection parts = md.Body.Parts;
  326. for (int i = 0; i < parts.Count; i++) {
  327. ExportDataContract (parts [i].XmlTypeMapping);
  328. method.Parameters.Add (
  329. new CodeParameterDeclarationExpression (
  330. new CodeTypeReference (parts [i].TypeName.Name),
  331. parts [i].Name));
  332. if (return_args)
  333. args [i] = new CodeArgumentReferenceExpression (parts [i].Name);
  334. }
  335. }
  336. return args;
  337. }
  338. #endregion
  339. [MonoTODO]
  340. public CodeTypeReference GenerateServiceEndpoint (
  341. ServiceEndpoint endpoint,
  342. out ChannelEndpointElement channelElement)
  343. {
  344. throw new NotImplementedException ();
  345. }
  346. private void ExportDataContract (XmlTypeMapping mapping)
  347. {
  348. if (mapping == null)
  349. return;
  350. QName qname = new QName (mapping.TypeName, mapping.Namespace);
  351. if (imported_names.ContainsKey (qname))
  352. return;
  353. CodeNamespace cns = new CodeNamespace ();
  354. XmlCodeExporter xce = new XmlCodeExporter (cns);
  355. xce.ExportTypeMapping (mapping);
  356. List <CodeTypeDeclaration> to_remove = new List <CodeTypeDeclaration> ();
  357. //Process the types just generated
  358. //FIXME: Iterate and assign the types to correct namespaces
  359. //At the end, add all those namespaces to the ccu
  360. foreach (CodeTypeDeclaration type in cns.Types) {
  361. string ns = GetXmlNamespace (type);
  362. if (ns == null)
  363. //FIXME: do what here?
  364. continue;
  365. QName type_name = new QName (type.Name, ns);
  366. if (imported_names.ContainsKey (type_name)) {
  367. //Type got reemitted, so remove it!
  368. to_remove.Add (type);
  369. continue;
  370. }
  371. imported_names [type_name] = type_name;
  372. type.Comments.Clear ();
  373. //Custom Attributes
  374. type.CustomAttributes.Clear ();
  375. if (type.IsEnum)
  376. continue;
  377. type.CustomAttributes.Add (
  378. new CodeAttributeDeclaration (
  379. new CodeTypeReference ("System.CodeDom.Compiler.GeneratedCodeAttribute"),
  380. new CodeAttributeArgument (new CodePrimitiveExpression ("System.Runtime.Serialization")),
  381. new CodeAttributeArgument (new CodePrimitiveExpression ("3.0.0.0"))));
  382. type.CustomAttributes.Add (
  383. new CodeAttributeDeclaration (
  384. new CodeTypeReference ("System.Runtime.Serialization.DataContractAttribute")));
  385. //BaseType and interface
  386. type.BaseTypes.Add (new CodeTypeReference (typeof (object)));
  387. type.BaseTypes.Add (new CodeTypeReference ("System.Runtime.Serialization.IExtensibleDataObject"));
  388. foreach (CodeTypeMember mbr in type.Members) {
  389. CodeMemberProperty p = mbr as CodeMemberProperty;
  390. if (p == null)
  391. continue;
  392. if ((p.Attributes & MemberAttributes.Public) == MemberAttributes.Public) {
  393. //FIXME: Clear all attributes or only XmlElementAttribute?
  394. p.CustomAttributes.Clear ();
  395. p.CustomAttributes.Add (new CodeAttributeDeclaration (
  396. new CodeTypeReference ("System.Runtime.Serialization.DataMemberAttribute")));
  397. p.Comments.Clear ();
  398. }
  399. }
  400. //Fields
  401. CodeMemberField field = new CodeMemberField (
  402. new CodeTypeReference ("System.Runtime.Serialization.ExtensionDataObject"),
  403. "extensionDataField");
  404. field.Attributes = MemberAttributes.Private | MemberAttributes.Final;
  405. type.Members.Add (field);
  406. //Property
  407. CodeMemberProperty prop = new CodeMemberProperty ();
  408. prop.Type = new CodeTypeReference ("System.Runtime.Serialization.ExtensionDataObject");
  409. prop.Name = "ExtensionData";
  410. prop.Attributes = MemberAttributes.Public | MemberAttributes.Final;
  411. //Get
  412. prop.GetStatements.Add (new CodeMethodReturnStatement (
  413. new CodeFieldReferenceExpression (
  414. new CodeThisReferenceExpression (),
  415. "extensionDataField")));
  416. //Set
  417. prop.SetStatements.Add (new CodeAssignStatement (
  418. new CodeFieldReferenceExpression (
  419. new CodeThisReferenceExpression (),
  420. "extensionDataField"),
  421. new CodePropertySetValueReferenceExpression ()));
  422. type.Members.Add (prop);
  423. }
  424. foreach (CodeTypeDeclaration type in to_remove)
  425. cns.Types.Remove (type);
  426. ccu.Namespaces.Add (cns);
  427. }
  428. private string GetXmlNamespace (CodeTypeDeclaration type)
  429. {
  430. foreach (CodeAttributeDeclaration attr in type.CustomAttributes) {
  431. if (attr.Name == "System.Xml.Serialization.XmlTypeAttribute" ||
  432. attr.Name == "System.Xml.Serialization.XmlRootAttribute") {
  433. foreach (CodeAttributeArgument arg in attr.Arguments)
  434. if (arg.Name == "Namespace")
  435. return ((CodePrimitiveExpression)arg.Value).Value as string;
  436. //Could not find Namespace arg!
  437. return null;
  438. }
  439. }
  440. return null;
  441. }
  442. }
  443. }