DataContractSerializerMessageContractImporter.cs 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  1. //
  2. // DataContractSerializerMessageContractImporter.cs
  3. //
  4. // Author: Atsushi Enomoto ([email protected])
  5. // Ankit Jain ([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.Runtime.Serialization;
  33. using System.ServiceModel;
  34. using System.ServiceModel.Channels;
  35. using System.ServiceModel.Dispatcher;
  36. using System.Text;
  37. using System.Web.Services.Description;
  38. using System.Xml;
  39. using System.Xml.Schema;
  40. using System.Xml.Serialization;
  41. using QName = System.Xml.XmlQualifiedName;
  42. using WSDL = System.Web.Services.Description.ServiceDescription;
  43. using Message = System.Web.Services.Description.Message;
  44. namespace System.ServiceModel.Description
  45. {
  46. public class DataContractSerializerMessageContractImporter
  47. : IWsdlImportExtension
  48. {
  49. MessageContractImporterInternal impl = new DataContractMessageContractImporterInternal ();
  50. bool enabled = true;
  51. public bool Enabled {
  52. get { return enabled; }
  53. set { enabled = value; }
  54. }
  55. void IWsdlImportExtension.BeforeImport (
  56. ServiceDescriptionCollection wsdlDocuments,
  57. XmlSchemaSet xmlSchemas,
  58. ICollection<XmlElement> policy)
  59. {
  60. if (!Enabled)
  61. return;
  62. impl.BeforeImport (wsdlDocuments, xmlSchemas, policy);
  63. }
  64. void IWsdlImportExtension.ImportContract (WsdlImporter importer,
  65. WsdlContractConversionContext context)
  66. {
  67. if (!Enabled)
  68. return;
  69. impl.ImportContract (importer, context);
  70. }
  71. void IWsdlImportExtension.ImportEndpoint (WsdlImporter importer,
  72. WsdlEndpointConversionContext context)
  73. {
  74. if (!Enabled)
  75. return;
  76. impl.ImportEndpoint (importer, context);
  77. }
  78. }
  79. abstract class MessageContractImporterInternal : IWsdlImportExtension
  80. {
  81. protected abstract void Init (WsdlImporter importer);
  82. public void ImportContract (WsdlImporter importer,
  83. WsdlContractConversionContext context)
  84. {
  85. if (importer == null)
  86. throw new ArgumentNullException ("importer");
  87. if (context == null)
  88. throw new ArgumentNullException ("context");
  89. if (this.importer != null || this.context != null)
  90. throw new SystemException ("INTERNAL ERROR: unexpected recursion of ImportContract method call");
  91. Init (importer);
  92. schema_set_in_use = new XmlSchemaSet ();
  93. schema_set_in_use.Add (importer.XmlSchemas);
  94. foreach (WSDL wsdl in importer.WsdlDocuments)
  95. foreach (XmlSchema xs in wsdl.Types.Schemas)
  96. schema_set_in_use.Add (xs);
  97. schema_set_in_use.Compile ();
  98. this.importer = importer;
  99. this.context = context;
  100. try {
  101. DoImportContract ();
  102. } finally {
  103. this.importer = null;
  104. this.context = null;
  105. }
  106. }
  107. internal WsdlImporter importer;
  108. WsdlContractConversionContext context;
  109. internal XmlSchemaSet schema_set_in_use;
  110. public void BeforeImport (
  111. ServiceDescriptionCollection wsdlDocuments,
  112. XmlSchemaSet xmlSchemas,
  113. ICollection<XmlElement> policy)
  114. {
  115. }
  116. void DoImportContract ()
  117. {
  118. PortType port_type = context.WsdlPortType;
  119. ContractDescription contract = context.Contract;
  120. int i, j;
  121. List<MessagePartDescription> parts = new List<MessagePartDescription> ();
  122. i = 0;
  123. foreach (Operation op in port_type.Operations) {
  124. OperationDescription opdescr = contract.Operations [i];
  125. if (IsOperationImported (port_type, op))
  126. continue;
  127. if (!CanImportOperation (port_type, op))
  128. continue;
  129. j = 0;
  130. foreach (OperationMessage opmsg in op.Messages) {
  131. //SM.MessageDescription
  132. MessageDescription msgdescr = opdescr.Messages [j];
  133. //OpMsg's corresponding WSMessage
  134. Message msg = port_type.ServiceDescription.Messages [opmsg.Message.Name];
  135. msgdescr.Body.WrapperNamespace = port_type.ServiceDescription.TargetNamespace;
  136. if (opmsg is OperationOutput) {
  137. //ReturnValue
  138. msg = port_type.ServiceDescription.Messages [opmsg.Message.Name];
  139. resolveMessage (msg, msgdescr.Body, parts);
  140. if (parts.Count > 0) {
  141. msgdescr.Body.ReturnValue = parts [0];
  142. parts.Clear ();
  143. }
  144. continue;
  145. }
  146. /* OperationInput */
  147. /* Parts, MessagePartDescription */
  148. resolveMessage (msg, msgdescr.Body, parts);
  149. foreach (MessagePartDescription p in parts)
  150. msgdescr.Body.Parts.Add (p);
  151. parts.Clear ();
  152. j ++;
  153. }
  154. OnOperationImported (opdescr);
  155. i ++;
  156. }
  157. }
  158. bool IsOperationImported (PortType pt, Operation op)
  159. {
  160. foreach (OperationMessage opmsg in op.Messages) {
  161. var opdsc = context.GetMessageDescription (opmsg);
  162. var parts = opdsc.Body.Parts;
  163. var ret = opdsc.Body.ReturnValue;
  164. if ((ret != null) &&
  165. (ret.DataContractImporter != null || ret.XmlSerializationImporter != null))
  166. return true;
  167. foreach (var part in opdsc.Body.Parts)
  168. if (part.DataContractImporter != null || part.XmlSerializationImporter != null)
  169. return true;
  170. }
  171. return false;
  172. }
  173. void resolveMessage (Message msg, MessageBodyDescription body, List<MessagePartDescription> parts)
  174. {
  175. foreach (MessagePart part in msg.Parts) {
  176. if (part.Name == "parameters") {
  177. if (!part.Element.IsEmpty) {
  178. body.WrapperName = part.Element.Name;
  179. ImportPartsBySchemaElement (part.Element, parts, msg, part);
  180. } else {
  181. body.WrapperName = part.Type.Name;
  182. ResolveType (part.Type, parts, body.WrapperNamespace);
  183. }
  184. }
  185. else
  186. throw new InvalidOperationException ("Only 'parameters' element in message part is supported"); // this should have been rejected by CanImportOperation().
  187. }
  188. }
  189. public void ImportEndpoint (WsdlImporter importer,
  190. WsdlEndpointConversionContext context)
  191. {
  192. }
  193. protected abstract void ImportPartsBySchemaElement (QName qname, List<MessagePartDescription> parts, Message msg, MessagePart part);
  194. protected abstract void ResolveType (QName qname, List<MessagePartDescription> parts, string ns);
  195. protected abstract bool CanImportOperation (PortType portType, Operation op);
  196. protected abstract void OnOperationImported (OperationDescription od);
  197. }
  198. class DataContractMessageContractImporterInternal : MessageContractImporterInternal
  199. {
  200. XsdDataContractImporter dc_importer;
  201. protected override void Init (WsdlImporter importer)
  202. {
  203. if (dc_importer == null)
  204. dc_importer = importer.GetState<XsdDataContractImporter> ();
  205. }
  206. protected override void ImportPartsBySchemaElement (QName qname, List<MessagePartDescription> parts, Message msg, MessagePart part)
  207. {
  208. XmlSchemaElement element = (XmlSchemaElement) schema_set_in_use.GlobalElements [qname];
  209. if (element == null)
  210. throw new InvalidOperationException ("Could not resolve : " + qname.ToString ()); // this should have been rejected by CanImportOperation().
  211. var ct = element.ElementSchemaType as XmlSchemaComplexType;
  212. if (ct == null) // simple type
  213. parts.Add (CreateMessagePart (element, msg, part));
  214. else // complex type
  215. foreach (var elem in GetElementsInParticle (ct.ContentTypeParticle))
  216. parts.Add (CreateMessagePart (elem, msg, part));
  217. }
  218. IEnumerable<XmlSchemaElement> GetElementsInParticle (XmlSchemaParticle p)
  219. {
  220. if (p is XmlSchemaElement) {
  221. yield return (XmlSchemaElement) p;
  222. } else {
  223. var gb = p as XmlSchemaGroupBase;
  224. if (gb != null)
  225. foreach (XmlSchemaParticle pp in gb.Items)
  226. foreach (var e in GetElementsInParticle (pp))
  227. yield return e;
  228. }
  229. }
  230. MessagePartDescription CreateMessagePart (XmlSchemaElement elem, Message msg, MessagePart msgPart)
  231. {
  232. var part = new MessagePartDescription (elem.QualifiedName.Name, elem.QualifiedName.Namespace);
  233. part.DataContractImporter = dc_importer;
  234. if (dc_importer.CanImport (schema_set_in_use, elem)) {
  235. var typeQName = dc_importer.Import (schema_set_in_use, elem);
  236. part.CodeTypeReference = dc_importer.GetCodeTypeReference (elem.ElementSchemaType.QualifiedName, elem);
  237. }
  238. return part;
  239. }
  240. protected override void ResolveType (QName qname, List<MessagePartDescription> parts, string ns)
  241. {
  242. /*foreach (XmlSchema xs in importer.Schemas)
  243. if (xs.Types [qname] != null)
  244. return resolveParameters ((XmlSchemaElement) xs.Types [qname]., msgdescr, importer);
  245. //FIXME: What to do here?
  246. throw new Exception ("Could not resolve : " + qname.ToString ());*/
  247. throw new NotImplementedException ();
  248. }
  249. Message FindMessage (OperationMessage om)
  250. {
  251. foreach (WSDL sd in importer.WsdlDocuments)
  252. if (sd.TargetNamespace == om.Message.Namespace)
  253. foreach (Message msg in sd.Messages)
  254. if (msg.Name == om.Message.Name)
  255. return msg;
  256. return null;
  257. }
  258. protected override bool CanImportOperation (PortType portType, Operation op)
  259. {
  260. foreach (OperationMessage om in op.Messages) {
  261. var msg = FindMessage (om);
  262. if (msg == null)
  263. return false;
  264. foreach (MessagePart part in msg.Parts) {
  265. if (part.Name == "parameters" && !part.Element.IsEmpty) {
  266. var xe = schema_set_in_use.GlobalElements [part.Element] as XmlSchemaElement;
  267. if (xe == null || !dc_importer.CanImport (schema_set_in_use, xe))
  268. return false;
  269. }
  270. else
  271. return false;
  272. }
  273. }
  274. return true;
  275. }
  276. protected override void OnOperationImported (OperationDescription od)
  277. {
  278. // do nothing
  279. }
  280. }
  281. class XmlSerializerMessageContractImporterInternal : MessageContractImporterInternal
  282. {
  283. CodeCompileUnit ccu;
  284. XmlSchemaSet schema_set_cache;
  285. XmlSchemaImporter schema_importer;
  286. XmlCodeExporter code_exporter;
  287. public CodeCompileUnit CodeCompileUnit {
  288. get { return ccu; }
  289. }
  290. protected override void Init (WsdlImporter importer)
  291. {
  292. if (ccu == null)
  293. ccu = importer.GetState<CodeCompileUnit> ();
  294. }
  295. protected override void ImportPartsBySchemaElement (QName qname, List<MessagePartDescription> parts, Message msg, MessagePart msgPart)
  296. {
  297. if (schema_set_cache != schema_set_in_use) {
  298. schema_set_cache = schema_set_in_use;
  299. var xss = new XmlSchemas ();
  300. foreach (XmlSchema xs in schema_set_cache.Schemas ())
  301. xss.Add (xs);
  302. schema_importer = new XmlSchemaImporter (xss);
  303. if (ccu.Namespaces.Count == 0)
  304. ccu.Namespaces.Add (new CodeNamespace ());
  305. var cns = ccu.Namespaces [0];
  306. code_exporter = new XmlCodeExporter (cns, ccu);
  307. }
  308. var part = new MessagePartDescription (qname.Name, qname.Namespace);
  309. part.XmlSerializationImporter = this;
  310. var mbrNS = msg.ServiceDescription.TargetNamespace;
  311. var xmm = schema_importer.ImportMembersMapping (qname);
  312. code_exporter.ExportMembersMapping (xmm);
  313. // FIXME: use of ElementName is a hack!
  314. part.CodeTypeReference = new CodeTypeReference (xmm.ElementName);
  315. parts.Add (part);
  316. }
  317. protected override void ResolveType (QName qname, List<MessagePartDescription> parts, string ns)
  318. {
  319. throw new NotImplementedException ();
  320. }
  321. protected override bool CanImportOperation (PortType portType, Operation op)
  322. {
  323. // FIXME: implement
  324. return true;
  325. }
  326. protected override void OnOperationImported (OperationDescription od)
  327. {
  328. od.Behaviors.Add (new XmlSerializerMappingBehavior ());
  329. }
  330. }
  331. // just a marker behavior
  332. class XmlSerializerMappingBehavior : IOperationBehavior
  333. {
  334. public void AddBindingParameters (OperationDescription operationDescription, BindingParameterCollection bindingParameters)
  335. {
  336. }
  337. public void ApplyClientBehavior (OperationDescription operationDescription, ClientOperation clientOperation)
  338. {
  339. }
  340. public void ApplyDispatchBehavior (OperationDescription operationDescription, DispatchOperation dispatchOperation)
  341. {
  342. }
  343. public void Validate (OperationDescription operationDescription)
  344. {
  345. }
  346. }
  347. }