2 // DataContractSerializerMessageContractImporter.cs
4 // Author: Atsushi Enomoto (atsushi@ximian.com)
5 // Ankit Jain (jankit@novell.com)
7 // Copyright (C) 2005 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.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;
37 using System.Web.Services.Description;
39 using System.Xml.Schema;
40 using System.Xml.Serialization;
42 using QName = System.Xml.XmlQualifiedName;
43 using WSDL = System.Web.Services.Description.ServiceDescription;
44 using Message = System.Web.Services.Description.Message;
46 namespace System.ServiceModel.Description
48 public class DataContractSerializerMessageContractImporter
49 : IWsdlImportExtension
51 MessageContractImporterInternal impl = new DataContractMessageContractImporterInternal ();
55 get { return enabled; }
56 set { enabled = value; }
59 void IWsdlImportExtension.BeforeImport (
60 ServiceDescriptionCollection wsdlDocuments,
61 XmlSchemaSet xmlSchemas,
62 ICollection<XmlElement> policy)
67 impl.BeforeImport (wsdlDocuments, xmlSchemas, policy);
70 void IWsdlImportExtension.ImportContract (WsdlImporter importer,
71 WsdlContractConversionContext context)
76 impl.ImportContract (importer, context);
79 void IWsdlImportExtension.ImportEndpoint (WsdlImporter importer,
80 WsdlEndpointConversionContext context)
85 impl.ImportEndpoint (importer, context);
89 abstract class MessageContractImporterInternal : IWsdlImportExtension
91 protected abstract void Init (WsdlImporter importer);
93 public void ImportContract (WsdlImporter importer,
94 WsdlContractConversionContext context)
97 throw new ArgumentNullException ("importer");
99 throw new ArgumentNullException ("context");
100 if (this.importer != null || this.context != null)
101 throw new SystemException ("INTERNAL ERROR: unexpected recursion of ImportContract method call");
105 schema_set_in_use = new XmlSchemaSet ();
106 schema_set_in_use.Add (importer.XmlSchemas);
107 foreach (WSDL wsdl in importer.WsdlDocuments)
108 foreach (XmlSchema xs in wsdl.Types.Schemas)
109 schema_set_in_use.Add (xs);
111 schema_set_in_use.Compile ();
113 this.importer = importer;
114 this.context = context;
118 this.importer = null;
123 internal WsdlImporter importer;
124 WsdlContractConversionContext context;
126 internal XmlSchemaSet schema_set_in_use;
128 public void BeforeImport (
129 ServiceDescriptionCollection wsdlDocuments,
130 XmlSchemaSet xmlSchemas,
131 ICollection<XmlElement> policy)
135 void DoImportContract ()
137 PortType port_type = context.WsdlPortType;
138 ContractDescription contract = context.Contract;
140 List<MessagePartDescription> parts = new List<MessagePartDescription> ();
143 foreach (Operation op in port_type.Operations) {
144 OperationDescription opdescr = contract.Operations [i];
145 if (IsOperationImported (port_type, op))
147 if (!CanImportOperation (port_type, op))
151 foreach (OperationMessage opmsg in op.Messages) {
152 //SM.MessageDescription
153 MessageDescription msgdescr = opdescr.Messages [j];
155 //OpMsg's corresponding WSMessage
156 Message msg = port_type.ServiceDescription.Messages [opmsg.Message.Name];
158 msgdescr.Body.WrapperNamespace = port_type.ServiceDescription.TargetNamespace;
160 if (opmsg is OperationOutput) {
162 msg = port_type.ServiceDescription.Messages [opmsg.Message.Name];
164 resolveMessage (msg, msgdescr.Body, parts);
165 if (parts.Count > 0) {
166 msgdescr.Body.ReturnValue = parts [0];
174 /* Parts, MessagePartDescription */
175 resolveMessage (msg, msgdescr.Body, parts);
176 foreach (MessagePartDescription p in parts)
177 msgdescr.Body.Parts.Add (p);
183 OnOperationImported (opdescr);
191 bool IsOperationImported (PortType pt, Operation op)
193 foreach (OperationMessage opmsg in op.Messages) {
195 var opdsc = context.GetMessageDescription (opmsg);
197 var parts = opdsc.Body.Parts;
198 var ret = opdsc.Body.ReturnValue;
201 (ret.DataContractImporter != null || ret.XmlSerializationImporter != null))
204 foreach (var part in opdsc.Body.Parts)
205 if (part.DataContractImporter != null || part.XmlSerializationImporter != null)
211 void resolveMessage (Message msg, MessageBodyDescription body, List<MessagePartDescription> parts)
213 foreach (MessagePart part in msg.Parts) {
214 if (part.Name == "parameters") {
215 if (!part.Element.IsEmpty) {
216 body.WrapperName = part.Element.Name;
217 ImportPartsBySchemaElement (part.Element, parts, msg, part);
219 body.WrapperName = part.Type.Name;
220 ResolveType (part.Type, parts, body.WrapperNamespace);
224 throw new InvalidOperationException ("Only 'parameters' element in message part is supported"); // this should have been rejected by CanImportOperation().
228 public void ImportEndpoint (WsdlImporter importer,
229 WsdlEndpointConversionContext context)
233 protected abstract void ImportPartsBySchemaElement (QName qname, List<MessagePartDescription> parts, Message msg, MessagePart part);
235 protected abstract void ResolveType (QName qname, List<MessagePartDescription> parts, string ns);
237 protected abstract bool CanImportOperation (PortType portType, Operation op);
239 protected abstract void OnOperationImported (OperationDescription od);
242 class DataContractMessageContractImporterInternal : MessageContractImporterInternal
244 XsdDataContractImporter dc_importer;
246 protected override void Init (WsdlImporter importer)
248 if (dc_importer == null)
249 dc_importer = importer.GetState<XsdDataContractImporter> ();
252 protected override void ImportPartsBySchemaElement (QName qname, List<MessagePartDescription> parts, Message msg, MessagePart part)
254 XmlSchemaElement element = (XmlSchemaElement) schema_set_in_use.GlobalElements [qname];
256 throw new InvalidOperationException ("Could not resolve : " + qname.ToString ()); // this should have been rejected by CanImportOperation().
258 var ct = element.ElementSchemaType as XmlSchemaComplexType;
259 if (ct == null) // simple type
260 parts.Add (CreateMessagePart (element, msg, part));
262 foreach (var elem in GetElementsInParticle (ct.ContentTypeParticle))
263 parts.Add (CreateMessagePart (elem, msg, part));
266 IEnumerable<XmlSchemaElement> GetElementsInParticle (XmlSchemaParticle p)
268 if (p is XmlSchemaElement) {
269 yield return (XmlSchemaElement) p;
271 var gb = p as XmlSchemaGroupBase;
273 foreach (XmlSchemaParticle pp in gb.Items)
274 foreach (var e in GetElementsInParticle (pp))
279 MessagePartDescription CreateMessagePart (XmlSchemaElement elem, Message msg, MessagePart msgPart)
281 var part = new MessagePartDescription (elem.QualifiedName.Name, elem.QualifiedName.Namespace);
282 part.DataContractImporter = dc_importer;
283 if (dc_importer.CanImport (schema_set_in_use, elem)) {
284 var typeQName = dc_importer.Import (schema_set_in_use, elem);
285 part.CodeTypeReference = dc_importer.GetCodeTypeReference (elem.ElementSchemaType.QualifiedName, elem);
290 protected override void ResolveType (QName qname, List<MessagePartDescription> parts, string ns)
292 /*foreach (XmlSchema xs in importer.Schemas)
293 if (xs.Types [qname] != null)
294 return resolveParameters ((XmlSchemaElement) xs.Types [qname]., msgdescr, importer);
296 //FIXME: What to do here?
297 throw new Exception ("Could not resolve : " + qname.ToString ());*/
298 throw new NotImplementedException ();
301 Message FindMessage (OperationMessage om)
303 foreach (WSDL sd in importer.WsdlDocuments)
304 if (sd.TargetNamespace == om.Message.Namespace)
305 foreach (Message msg in sd.Messages)
306 if (msg.Name == om.Message.Name)
311 protected override bool CanImportOperation (PortType portType, Operation op)
313 foreach (OperationMessage om in op.Messages) {
314 var msg = FindMessage (om);
317 foreach (MessagePart part in msg.Parts) {
318 if (part.Name == "parameters" && !part.Element.IsEmpty) {
319 var xe = schema_set_in_use.GlobalElements [part.Element] as XmlSchemaElement;
320 if (xe == null || !dc_importer.CanImport (schema_set_in_use, xe))
330 protected override void OnOperationImported (OperationDescription od)
336 class XmlSerializerMessageContractImporterInternal : MessageContractImporterInternal
339 XmlSchemaSet schema_set_cache;
340 XmlSchemaImporter schema_importer;
341 XmlCodeExporter code_exporter;
343 public CodeCompileUnit CodeCompileUnit {
347 protected override void Init (WsdlImporter importer)
350 ccu = importer.GetState<CodeCompileUnit> ();
353 protected override void ImportPartsBySchemaElement (QName qname, List<MessagePartDescription> parts, Message msg, MessagePart msgPart)
355 if (schema_set_cache != schema_set_in_use) {
356 schema_set_cache = schema_set_in_use;
357 var xss = new XmlSchemas ();
358 foreach (XmlSchema xs in schema_set_cache.Schemas ())
360 schema_importer = new XmlSchemaImporter (xss);
361 if (ccu.Namespaces.Count == 0)
362 ccu.Namespaces.Add (new CodeNamespace ());
363 var cns = ccu.Namespaces [0];
364 code_exporter = new XmlCodeExporter (cns, ccu);
367 var part = new MessagePartDescription (qname.Name, qname.Namespace);
368 part.XmlSerializationImporter = this;
369 var mbrNS = msg.ServiceDescription.TargetNamespace;
370 var xmm = schema_importer.ImportMembersMapping (qname);
371 code_exporter.ExportMembersMapping (xmm);
372 // FIXME: use of ElementName is a hack!
373 part.CodeTypeReference = new CodeTypeReference (xmm.ElementName);
377 protected override void ResolveType (QName qname, List<MessagePartDescription> parts, string ns)
379 throw new NotImplementedException ();
382 protected override bool CanImportOperation (PortType portType, Operation op)
388 protected override void OnOperationImported (OperationDescription od)
390 od.Behaviors.Add (new XmlSerializerMappingBehavior ());
394 // just a marker behavior
395 class XmlSerializerMappingBehavior : IOperationBehavior
397 public void AddBindingParameters (OperationDescription operationDescription, BindingParameterCollection bindingParameters)
401 public void ApplyClientBehavior (OperationDescription operationDescription, ClientOperation clientOperation)
405 public void ApplyDispatchBehavior (OperationDescription operationDescription, DispatchOperation dispatchOperation)
409 public void Validate (OperationDescription operationDescription)