1 //------------------------------------------------------------------------------
2 // <copyright file="SoapReflectionImporter.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">[....]</owner>
6 //------------------------------------------------------------------------------
8 namespace System.Xml.Serialization {
10 using System.Reflection;
12 using System.Globalization;
13 using System.Xml.Schema;
14 using System.Collections;
15 using System.ComponentModel;
16 using System.Threading;
18 /// <include file='doc\SoapReflectionImporter.uex' path='docs/doc[@for="SoapReflectionImporter"]/*' />
20 /// <para>[To be supplied.]</para>
22 public class SoapReflectionImporter {
24 SoapAttributeOverrides attributeOverrides;
25 NameTable types = new NameTable(); // xmltypename + xmlns -> Mapping
26 NameTable nullables = new NameTable(); // xmltypename + xmlns -> NullableMapping
29 ModelScope modelScope;
32 /// <include file='doc\SoapReflectionImporter.uex' path='docs/doc[@for="SoapReflectionImporter.SoapReflectionImporter"]/*' />
34 /// <para>[To be supplied.]</para>
36 public SoapReflectionImporter() : this(null, null) {
39 /// <include file='doc\SoapReflectionImporter.uex' path='docs/doc[@for="SoapReflectionImporter.SoapReflectionImporter1"]/*' />
41 /// <para>[To be supplied.]</para>
43 public SoapReflectionImporter(string defaultNamespace) : this(null, defaultNamespace) {
46 /// <include file='doc\SoapReflectionImporter.uex' path='docs/doc[@for="SoapReflectionImporter.SoapReflectionImporter2"]/*' />
48 /// <para>[To be supplied.]</para>
50 public SoapReflectionImporter(SoapAttributeOverrides attributeOverrides) : this(attributeOverrides, null) {
53 /// <include file='doc\SoapReflectionImporter.uex' path='docs/doc[@for="SoapReflectionImporter.SoapReflectionImporter3"]/*' />
55 /// <para>[To be supplied.]</para>
57 public SoapReflectionImporter(SoapAttributeOverrides attributeOverrides, string defaultNamespace) {
58 if (defaultNamespace == null)
59 defaultNamespace = String.Empty;
60 if (attributeOverrides == null)
61 attributeOverrides = new SoapAttributeOverrides();
62 this.attributeOverrides = attributeOverrides;
63 this.defaultNs = defaultNamespace;
64 this.typeScope = new TypeScope();
65 this.modelScope = new ModelScope(this.typeScope);
68 /// <include file='doc\SoapReflectionImporter.uex' path='docs/doc[@for="SoapReflectionImporter.IncludeTypes"]/*' />
70 /// <para>[To be supplied.]</para>
72 public void IncludeTypes(ICustomAttributeProvider provider) {
73 IncludeTypes(provider, new RecursionLimiter());
76 void IncludeTypes(ICustomAttributeProvider provider, RecursionLimiter limiter) {
77 object[] attrs = provider.GetCustomAttributes(typeof(SoapIncludeAttribute), false);
78 for (int i = 0; i < attrs.Length; i++) {
79 IncludeType(((SoapIncludeAttribute)attrs[i]).Type, limiter);
83 /// <include file='doc\SoapReflectionImporter.uex' path='docs/doc[@for="SoapReflectionImporter.IncludeType"]/*' />
85 /// <para>[To be supplied.]</para>
87 public void IncludeType(Type type) {
88 IncludeType(type, new RecursionLimiter());
91 void IncludeType(Type type, RecursionLimiter limiter) {
92 ImportTypeMapping(modelScope.GetTypeModel(type), limiter);
95 /// <include file='doc\SoapReflectionImporter.uex' path='docs/doc[@for="XmlReflectionImporter.ImportTypeMapping"]/*' />
97 /// <para>[To be supplied.]</para>
99 public XmlTypeMapping ImportTypeMapping(Type type) {
100 return ImportTypeMapping(type, null);
103 /// <include file='doc\SoapReflectionImporter.uex' path='docs/doc[@for="XmlReflectionImporter.ImportTypeMapping1"]/*' />
105 /// <para>[To be supplied.]</para>
107 public XmlTypeMapping ImportTypeMapping(Type type, string defaultNamespace) {
108 ElementAccessor element = new ElementAccessor();
109 element.IsSoap = true;
110 element.Mapping = ImportTypeMapping(modelScope.GetTypeModel(type), new RecursionLimiter());
111 element.Name = element.Mapping.DefaultElementName;
112 element.Namespace = element.Mapping.Namespace == null ? defaultNamespace : element.Mapping.Namespace;
113 element.Form = XmlSchemaForm.Qualified;
114 XmlTypeMapping xmlMapping = new XmlTypeMapping(typeScope, element);
115 xmlMapping.SetKeyInternal(XmlMapping.GenerateKey(type, null, defaultNamespace));
116 xmlMapping.IsSoap = true;
117 xmlMapping.GenerateSerializer = true;
121 /// <include file='doc\SoapReflectionImporter.uex' path='docs/doc[@for="SoapReflectionImporter.ImportMembersMapping"]/*' />
123 /// <para>[To be supplied.]</para>
125 public XmlMembersMapping ImportMembersMapping(string elementName, string ns, XmlReflectionMember[] members) {
126 return ImportMembersMapping(elementName, ns, members, true, true, false);
129 /// <include file='doc\SoapReflectionImporter.uex' path='docs/doc[@for="SoapReflectionImporter.ImportMembersMapping1"]/*' />
131 /// <para>[To be supplied.]</para>
133 public XmlMembersMapping ImportMembersMapping(string elementName, string ns, XmlReflectionMember[] members, bool hasWrapperElement, bool writeAccessors) {
134 return ImportMembersMapping(elementName, ns, members, hasWrapperElement, writeAccessors, false);
137 /// <include file='doc\SoapReflectionImporter.uex' path='docs/doc[@for="SoapReflectionImporter.ImportMembersMapping2"]/*' />
139 /// <para>[To be supplied.]</para>
141 public XmlMembersMapping ImportMembersMapping(string elementName, string ns, XmlReflectionMember[] members, bool hasWrapperElement, bool writeAccessors, bool validate) {
142 return ImportMembersMapping(elementName, ns, members, hasWrapperElement, writeAccessors, validate, XmlMappingAccess.Read | XmlMappingAccess.Write);
144 /// <include file='doc\SoapReflectionImporter.uex' path='docs/doc[@for="SoapReflectionImporter.ImportMembersMapping3"]/*' />
146 /// <para>[To be supplied.]</para>
148 public XmlMembersMapping ImportMembersMapping(string elementName, string ns, XmlReflectionMember[] members, bool hasWrapperElement, bool writeAccessors, bool validate, XmlMappingAccess access) {
149 ElementAccessor element = new ElementAccessor();
150 element.IsSoap = true;
151 element.Name = elementName == null || elementName.Length == 0 ? elementName : XmlConvert.EncodeLocalName(elementName);
153 element.Mapping = ImportMembersMapping(members, ns, hasWrapperElement, writeAccessors, validate, new RecursionLimiter());
154 element.Mapping.TypeName = elementName;
155 element.Namespace = element.Mapping.Namespace == null ? ns : element.Mapping.Namespace;
156 element.Form = XmlSchemaForm.Qualified;
157 XmlMembersMapping xmlMapping = new XmlMembersMapping(typeScope, element, access);
158 xmlMapping.IsSoap = true;
159 xmlMapping.GenerateSerializer = true;
163 Exception ReflectionException(string context, Exception e) {
164 return new InvalidOperationException(Res.GetString(Res.XmlReflectionError, context), e);
167 SoapAttributes GetAttributes(Type type) {
168 SoapAttributes attrs = attributeOverrides[type];
169 if (attrs != null) return attrs;
170 return new SoapAttributes(type);
173 SoapAttributes GetAttributes(MemberInfo memberInfo) {
174 SoapAttributes attrs = attributeOverrides[memberInfo.DeclaringType, memberInfo.Name];
175 if (attrs != null) return attrs;
176 return new SoapAttributes(memberInfo);
179 TypeMapping ImportTypeMapping(TypeModel model, RecursionLimiter limiter) {
180 return ImportTypeMapping(model, String.Empty, limiter);
183 TypeMapping ImportTypeMapping(TypeModel model, string dataType, RecursionLimiter limiter) {
184 if (dataType.Length > 0) {
185 if (!model.TypeDesc.IsPrimitive) {
186 throw new InvalidOperationException(Res.GetString(Res.XmlInvalidDataTypeUsage, dataType, "SoapElementAttribute.DataType"));
188 TypeDesc td = typeScope.GetTypeDesc(dataType, XmlSchema.Namespace);
190 throw new InvalidOperationException(Res.GetString(Res.XmlInvalidXsdDataType, dataType, "SoapElementAttribute.DataType", new XmlQualifiedName(dataType, XmlSchema.Namespace).ToString()));
192 if (model.TypeDesc.FullName != td.FullName) {
193 throw new InvalidOperationException(Res.GetString(Res.XmlDataTypeMismatch, dataType, "SoapElementAttribute.DataType", model.TypeDesc.FullName));
197 SoapAttributes a = GetAttributes(model.Type);
199 if ((a.SoapFlags & ~SoapAttributeFlags.Type) != 0)
200 throw new InvalidOperationException(Res.GetString(Res.XmlInvalidTypeAttributes, model.Type.FullName));
202 switch (model.TypeDesc.Kind) {
204 return ImportEnumMapping((EnumModel)model);
205 case TypeKind.Primitive:
206 return ImportPrimitiveMapping((PrimitiveModel)model, dataType);
208 case TypeKind.Collection:
209 case TypeKind.Enumerable:
210 return ImportArrayLikeMapping((ArrayModel)model, limiter);
213 case TypeKind.Struct:
214 if (model.TypeDesc.IsOptionalValue) {
215 TypeDesc baseTypeDesc = model.TypeDesc.BaseTypeDesc;
216 SoapAttributes baseAttributes = GetAttributes(baseTypeDesc.Type);
217 string typeNs = defaultNs;
218 if (baseAttributes.SoapType != null && baseAttributes.SoapType.Namespace != null)
219 typeNs = baseAttributes.SoapType.Namespace;
220 TypeDesc valueTypeDesc = string.IsNullOrEmpty(dataType) ? model.TypeDesc.BaseTypeDesc : typeScope.GetTypeDesc(dataType, XmlSchema.Namespace);
221 string xsdTypeName = string.IsNullOrEmpty(dataType) ? model.TypeDesc.BaseTypeDesc.Name : dataType;
222 TypeMapping baseMapping = GetTypeMapping(xsdTypeName, typeNs, valueTypeDesc);
223 if (baseMapping == null)
224 baseMapping = ImportTypeMapping(modelScope.GetTypeModel(baseTypeDesc.Type), dataType, limiter);
225 return CreateNullableMapping(baseMapping, model.TypeDesc.Type);
228 return ImportStructLikeMapping((StructModel)model, limiter);
231 throw new NotSupportedException(Res.GetString(Res.XmlUnsupportedSoapTypeKind, model.TypeDesc.FullName));
235 StructMapping CreateRootMapping() {
236 TypeDesc typeDesc = typeScope.GetTypeDesc(typeof(object));
237 StructMapping mapping = new StructMapping();
238 mapping.IsSoap = true;
239 mapping.TypeDesc = typeDesc;
240 mapping.Members = new MemberMapping[0];
241 mapping.IncludeInSchema = false;
242 mapping.TypeName = Soap.UrType;
243 mapping.Namespace = XmlSchema.Namespace;
247 StructMapping GetRootMapping() {
249 root = CreateRootMapping();
250 typeScope.AddTypeMapping(root);
255 TypeMapping GetTypeMapping(string typeName, string ns, TypeDesc typeDesc) {
256 TypeMapping mapping = (TypeMapping)types[typeName, ns];
257 if (mapping == null) return null;
258 if (mapping.TypeDesc != typeDesc)
259 throw new InvalidOperationException(Res.GetString(Res.XmlTypesDuplicate, typeDesc.FullName, mapping.TypeDesc.FullName, typeName, ns));
263 NullableMapping CreateNullableMapping(TypeMapping baseMapping, Type type) {
264 TypeDesc typeDesc = baseMapping.TypeDesc.GetNullableTypeDesc(type);
265 TypeMapping existingMapping = (TypeMapping)nullables[baseMapping.TypeName, baseMapping.Namespace];
266 NullableMapping mapping;
267 if (existingMapping != null) {
268 if (existingMapping is NullableMapping) {
269 mapping = (NullableMapping)existingMapping;
270 if (mapping.BaseMapping is PrimitiveMapping && baseMapping is PrimitiveMapping)
272 else if (mapping.BaseMapping == baseMapping) {
276 throw new InvalidOperationException(Res.GetString(Res.XmlTypesDuplicate, typeDesc.FullName, existingMapping.TypeDesc.FullName, typeDesc.Name, existingMapping.Namespace));
279 else if (!(baseMapping is PrimitiveMapping)){
280 throw new InvalidOperationException(Res.GetString(Res.XmlTypesDuplicate, typeDesc.FullName, existingMapping.TypeDesc.FullName, typeDesc.Name, existingMapping.Namespace));
283 mapping = new NullableMapping();
284 mapping.BaseMapping = baseMapping;
285 mapping.TypeDesc = typeDesc;
286 mapping.TypeName = baseMapping.TypeName;
287 mapping.Namespace = baseMapping.Namespace;
288 mapping.IncludeInSchema = false; //baseMapping.IncludeInSchema;
289 nullables.Add(baseMapping.TypeName, mapping.Namespace, mapping);
290 typeScope.AddTypeMapping(mapping);
294 StructMapping ImportStructLikeMapping(StructModel model, RecursionLimiter limiter) {
295 if (model.TypeDesc.Kind == TypeKind.Root) return GetRootMapping();
297 SoapAttributes a = GetAttributes(model.Type);
299 string typeNs = defaultNs;
300 if (a.SoapType != null && a.SoapType.Namespace != null)
301 typeNs = a.SoapType.Namespace;
302 string typeName = XsdTypeName(model.Type, a, model.TypeDesc.Name);
303 typeName = XmlConvert.EncodeLocalName(typeName);
305 StructMapping mapping = (StructMapping)GetTypeMapping(typeName, typeNs, model.TypeDesc);
306 if (mapping == null) {
307 mapping = new StructMapping();
308 mapping.IsSoap = true;
309 mapping.TypeDesc = model.TypeDesc;
310 mapping.Namespace = typeNs;
311 mapping.TypeName = typeName;
312 if (a.SoapType != null) mapping.IncludeInSchema = a.SoapType.IncludeInSchema;
313 typeScope.AddTypeMapping(mapping);
314 types.Add(typeName, typeNs, mapping);
315 if (limiter.IsExceededLimit) {
316 limiter.DeferredWorkItems.Add(new ImportStructWorkItem(model, mapping));
322 InitializeStructMembers(mapping, model, limiter);
323 while (limiter.DeferredWorkItems.Count > 0) {
324 int index = limiter.DeferredWorkItems.Count - 1;
325 ImportStructWorkItem item = limiter.DeferredWorkItems[index];
326 if (InitializeStructMembers(item.Mapping, item.Model, limiter)) {
328 // if InitializeStructMembers returns true, then there were *no* chages to the DeferredWorkItems
331 // use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe
332 if (index != limiter.DeferredWorkItems.Count - 1)
333 throw new InvalidOperationException(Res.GetString(Res.XmlInternalErrorDetails, "DeferredWorkItems.Count have changed"));
334 if (item != limiter.DeferredWorkItems[index])
335 throw new InvalidOperationException(Res.GetString(Res.XmlInternalErrorDetails, "DeferredWorkItems.Top have changed"));
337 // Remove the last work item
338 limiter.DeferredWorkItems.RemoveAt(index);
347 bool InitializeStructMembers(StructMapping mapping, StructModel model, RecursionLimiter limiter) {
348 if (mapping.IsFullyInitialized)
350 if (model.TypeDesc.BaseTypeDesc != null) {
351 StructMapping baseMapping = ImportStructLikeMapping((StructModel)modelScope.GetTypeModel(model.Type.BaseType, false), limiter);
353 // check to see if the import of the baseMapping was deffered
354 int baseIndex = limiter.DeferredWorkItems.IndexOf(mapping.BaseMapping);
356 mapping.BaseMapping = baseMapping;
359 // the import of the baseMapping was deffered, make sure that the derived mappings is deffered as well
360 if (!limiter.DeferredWorkItems.Contains(mapping)) {
361 limiter.DeferredWorkItems.Add(new ImportStructWorkItem(model, mapping));
363 // make sure that baseMapping get processed before the derived
364 int top = limiter.DeferredWorkItems.Count-1;
365 if (baseIndex < top) {
366 ImportStructWorkItem baseMappingWorkItem = limiter.DeferredWorkItems[baseIndex];
367 limiter.DeferredWorkItems[baseIndex] = limiter.DeferredWorkItems[top];
368 limiter.DeferredWorkItems[top] = baseMappingWorkItem;
373 ArrayList members = new ArrayList();
374 foreach (MemberInfo memberInfo in model.GetMemberInfos()) {
375 if ((memberInfo.MemberType & (MemberTypes.Field | MemberTypes.Property)) == 0)
377 SoapAttributes memberAttrs = GetAttributes(memberInfo);
378 if (memberAttrs.SoapIgnore) continue;
379 FieldModel fieldModel = model.GetFieldModel(memberInfo);
380 if (fieldModel == null) continue;
381 MemberMapping member = ImportFieldMapping(fieldModel, memberAttrs, mapping.Namespace, limiter);
382 if (member == null) continue;
384 if (!member.TypeDesc.IsPrimitive && !member.TypeDesc.IsEnum && !member.TypeDesc.IsOptionalValue) {
385 if (model.TypeDesc.IsValueType)
386 throw new NotSupportedException(Res.GetString(Res.XmlRpcRefsInValueType, model.TypeDesc.FullName));
387 if (member.TypeDesc.IsValueType)
388 throw new NotSupportedException(Res.GetString(Res.XmlRpcNestedValueType, member.TypeDesc.FullName));
390 if (mapping.BaseMapping != null) {
391 if (mapping.BaseMapping.Declares(member, mapping.TypeName)) continue;
395 mapping.Members = (MemberMapping[])members.ToArray(typeof(MemberMapping));
396 if (mapping.BaseMapping == null) mapping.BaseMapping = GetRootMapping();
397 IncludeTypes(model.Type, limiter);
403 ArrayMapping ImportArrayLikeMapping(ArrayModel model, RecursionLimiter limiter) {
405 ArrayMapping mapping = new ArrayMapping();
406 mapping.IsSoap = true;
407 TypeMapping itemTypeMapping = ImportTypeMapping(model.Element, limiter);
409 if (itemTypeMapping.TypeDesc.IsValueType && !itemTypeMapping.TypeDesc.IsPrimitive && !itemTypeMapping.TypeDesc.IsEnum)
410 throw new NotSupportedException(Res.GetString(Res.XmlRpcArrayOfValueTypes, model.TypeDesc.FullName));
412 mapping.TypeDesc = model.TypeDesc;
413 mapping.Elements = new ElementAccessor[] {
414 CreateElementAccessor(itemTypeMapping, mapping.Namespace) };
415 SetArrayMappingType(mapping);
417 // in the case of an ArrayMapping we can have more that one mapping correspond to a type
418 // examples of that are ArrayList and object[] both will map tp ArrayOfur-type
419 // so we create a link list for all mappings of the same XSD type
420 ArrayMapping existingMapping = (ArrayMapping)types[mapping.TypeName, mapping.Namespace];
421 if (existingMapping != null) {
422 ArrayMapping first = existingMapping;
423 while (existingMapping != null) {
424 if (existingMapping.TypeDesc == model.TypeDesc)
425 return existingMapping;
426 existingMapping = existingMapping.Next;
428 mapping.Next = first;
429 types[mapping.TypeName, mapping.Namespace] = mapping;
432 typeScope.AddTypeMapping(mapping);
433 types.Add(mapping.TypeName, mapping.Namespace, mapping);
434 IncludeTypes(model.Type);
439 void SetArrayMappingType(ArrayMapping mapping) {
440 bool useDefaultNs = false;
443 string itemTypeNamespace;
445 TypeMapping itemTypeMapping;
446 if (mapping.Elements.Length == 1)
447 itemTypeMapping = mapping.Elements[0].Mapping;
449 itemTypeMapping = null;
451 if (itemTypeMapping is EnumMapping) {
452 itemTypeNamespace = itemTypeMapping.Namespace;
453 itemTypeName = itemTypeMapping.TypeName;
455 else if (itemTypeMapping is PrimitiveMapping) {
456 itemTypeNamespace = itemTypeMapping.TypeDesc.IsXsdType ? XmlSchema.Namespace : UrtTypes.Namespace;
457 itemTypeName = itemTypeMapping.TypeDesc.DataType.Name;
460 else if (itemTypeMapping is StructMapping) {
461 if (itemTypeMapping.TypeDesc.IsRoot) {
462 itemTypeNamespace = XmlSchema.Namespace;
463 itemTypeName = Soap.UrType;
467 itemTypeNamespace = itemTypeMapping.Namespace;
468 itemTypeName = itemTypeMapping.TypeName;
471 else if (itemTypeMapping is ArrayMapping) {
472 itemTypeNamespace = itemTypeMapping.Namespace;
473 itemTypeName = itemTypeMapping.TypeName;
476 throw new InvalidOperationException(Res.GetString(Res.XmlInvalidSoapArray, mapping.TypeDesc.FullName));
479 itemTypeName = CodeIdentifier.MakePascal(itemTypeName);
480 string uniqueName = "ArrayOf" + itemTypeName;
481 string ns = useDefaultNs ? defaultNs : itemTypeNamespace;
483 TypeMapping existingMapping = (TypeMapping)types[uniqueName, ns];
484 while (existingMapping != null) {
485 if (existingMapping is ArrayMapping) {
486 ArrayMapping arrayMapping = (ArrayMapping)existingMapping;
487 if (AccessorMapping.ElementsMatch(arrayMapping.Elements, mapping.Elements)) {
491 // need to re-name the mapping
492 uniqueName = itemTypeName + i.ToString(CultureInfo.InvariantCulture);
493 existingMapping = (TypeMapping)types[uniqueName, ns];
496 mapping.Namespace = ns;
497 mapping.TypeName = uniqueName;
500 PrimitiveMapping ImportPrimitiveMapping(PrimitiveModel model, string dataType) {
501 PrimitiveMapping mapping = new PrimitiveMapping();
502 mapping.IsSoap = true;
503 if (dataType.Length > 0) {
504 mapping.TypeDesc = typeScope.GetTypeDesc(dataType, XmlSchema.Namespace);
505 if (mapping.TypeDesc == null) {
506 // try it as a non-Xsd type
507 mapping.TypeDesc = typeScope.GetTypeDesc(dataType, UrtTypes.Namespace);
508 if (mapping.TypeDesc == null) {
509 throw new InvalidOperationException(Res.GetString(Res.XmlUdeclaredXsdType, dataType));
514 mapping.TypeDesc = model.TypeDesc;
516 mapping.TypeName = mapping.TypeDesc.DataType.Name;
517 mapping.Namespace = mapping.TypeDesc.IsXsdType ? XmlSchema.Namespace : UrtTypes.Namespace;
521 EnumMapping ImportEnumMapping(EnumModel model) {
522 SoapAttributes a = GetAttributes(model.Type);
523 string typeNs = defaultNs;
524 if (a.SoapType != null && a.SoapType.Namespace != null)
525 typeNs = a.SoapType.Namespace;
526 string typeName = XsdTypeName(model.Type, a, model.TypeDesc.Name);
527 typeName = XmlConvert.EncodeLocalName(typeName);
529 EnumMapping mapping = (EnumMapping)GetTypeMapping(typeName, typeNs, model.TypeDesc);
530 if (mapping == null) {
531 mapping = new EnumMapping();
532 mapping.IsSoap = true;
533 mapping.TypeDesc = model.TypeDesc;
534 mapping.TypeName = typeName;
535 mapping.Namespace = typeNs;
536 mapping.IsFlags = model.Type.IsDefined(typeof(FlagsAttribute), false);
537 typeScope.AddTypeMapping(mapping);
538 types.Add(typeName, typeNs, mapping);
539 ArrayList constants = new ArrayList();
540 for (int i = 0; i < model.Constants.Length; i++) {
541 ConstantMapping constant = ImportConstantMapping(model.Constants[i]);
542 if (constant != null) constants.Add(constant);
544 if (constants.Count == 0) {
545 throw new InvalidOperationException(Res.GetString(Res.XmlNoSerializableMembers, model.TypeDesc.FullName));
547 mapping.Constants = (ConstantMapping[])constants.ToArray(typeof(ConstantMapping));
552 ConstantMapping ImportConstantMapping(ConstantModel model) {
553 SoapAttributes a = GetAttributes(model.FieldInfo);
554 if (a.SoapIgnore) return null;
555 if ((a.SoapFlags & ~SoapAttributeFlags.Enum) != 0)
556 throw new InvalidOperationException(Res.GetString(Res.XmlInvalidEnumAttribute));
557 if (a.SoapEnum == null)
558 a.SoapEnum = new SoapEnumAttribute();
560 ConstantMapping constant = new ConstantMapping();
561 constant.XmlName = a.SoapEnum.Name.Length == 0 ? model.Name : a.SoapEnum.Name;
562 constant.Name = model.Name;
563 constant.Value = model.Value;
567 MembersMapping ImportMembersMapping(XmlReflectionMember[] xmlReflectionMembers, string ns, bool hasWrapperElement, bool writeAccessors, bool validateWrapperElement, RecursionLimiter limiter) {
568 MembersMapping members = new MembersMapping();
569 members.TypeDesc = typeScope.GetTypeDesc(typeof(object[]));
570 MemberMapping[] mappings = new MemberMapping[xmlReflectionMembers.Length];
571 for (int i = 0; i < mappings.Length; i++) {
573 XmlReflectionMember member = xmlReflectionMembers[i];
574 MemberMapping mapping = ImportMemberMapping(member, ns, xmlReflectionMembers, hasWrapperElement ? XmlSchemaForm.Unqualified : XmlSchemaForm.Qualified, limiter);
575 if (member.IsReturnValue && writeAccessors) { // no special treatment for return values with doc/enc
576 if (i > 0) throw new InvalidOperationException(Res.GetString(Res.XmlInvalidReturnPosition));
577 mapping.IsReturnValue = true;
579 mappings[i] = mapping;
581 catch (Exception e) {
582 if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {
585 throw ReflectionException(xmlReflectionMembers[i].MemberName, e);
588 members.Members = mappings;
589 members.HasWrapperElement = hasWrapperElement;
590 if (hasWrapperElement) {
591 members.ValidateRpcWrapperElement = validateWrapperElement;
593 members.WriteAccessors = writeAccessors;
594 members.IsSoap = true;
595 if (hasWrapperElement && !writeAccessors)
596 members.Namespace = ns;
600 MemberMapping ImportMemberMapping(XmlReflectionMember xmlReflectionMember, string ns, XmlReflectionMember[] xmlReflectionMembers, XmlSchemaForm form, RecursionLimiter limiter) {
601 SoapAttributes a = xmlReflectionMember.SoapAttributes;
602 if (a.SoapIgnore) return null;
603 MemberMapping member = new MemberMapping();
604 member.IsSoap = true;
605 member.Name = xmlReflectionMember.MemberName;
606 bool checkSpecified = XmlReflectionImporter.FindSpecifiedMember(xmlReflectionMember.MemberName, xmlReflectionMembers) != null;
607 FieldModel model = new FieldModel(xmlReflectionMember.MemberName, xmlReflectionMember.MemberType, typeScope.GetTypeDesc(xmlReflectionMember.MemberType), checkSpecified, false);
608 member.CheckShouldPersist = model.CheckShouldPersist;
609 member.CheckSpecified = model.CheckSpecified;
610 member.ReadOnly = model.ReadOnly; // || !model.FieldTypeDesc.HasDefaultConstructor;
611 ImportAccessorMapping(member, model, a, ns, form, limiter);
612 if (xmlReflectionMember.OverrideIsNullable)
613 member.Elements[0].IsNullable = false;
617 MemberMapping ImportFieldMapping(FieldModel model, SoapAttributes a, string ns, RecursionLimiter limiter) {
618 if (a.SoapIgnore) return null;
619 MemberMapping member = new MemberMapping();
620 member.IsSoap = true;
621 member.Name = model.Name;
622 member.CheckShouldPersist = model.CheckShouldPersist;
623 member.CheckSpecified = model.CheckSpecified;
624 member.MemberInfo = model.MemberInfo;
625 member.CheckSpecifiedMemberInfo = model.CheckSpecifiedMemberInfo;
626 member.CheckShouldPersistMethodInfo = model.CheckShouldPersistMethodInfo;
627 member.ReadOnly = model.ReadOnly; // || !model.FieldTypeDesc.HasDefaultConstructor;
628 ImportAccessorMapping(member, model, a, ns, XmlSchemaForm.Unqualified, limiter);
632 void ImportAccessorMapping(MemberMapping accessor, FieldModel model, SoapAttributes a, string ns, XmlSchemaForm form, RecursionLimiter limiter) {
633 Type accessorType = model.FieldType;
634 string accessorName = model.Name;
635 accessor.TypeDesc = typeScope.GetTypeDesc(accessorType);
636 if (accessor.TypeDesc.IsVoid) {
637 throw new InvalidOperationException(Res.GetString(Res.XmlInvalidVoid));
640 SoapAttributeFlags flags = a.SoapFlags;
641 if ((flags & SoapAttributeFlags.Attribute) == SoapAttributeFlags.Attribute) {
642 if (!accessor.TypeDesc.IsPrimitive && !accessor.TypeDesc.IsEnum)
643 throw new InvalidOperationException(Res.GetString(Res.XmlIllegalSoapAttribute, accessorName, accessor.TypeDesc.FullName));
645 if ((flags & SoapAttributeFlags.Attribute) != flags)
646 throw new InvalidOperationException(Res.GetString(Res.XmlInvalidElementAttribute));
648 AttributeAccessor attribute = new AttributeAccessor();
649 attribute.Name = Accessor.EscapeQName(a.SoapAttribute == null || a.SoapAttribute.AttributeName.Length == 0 ? accessorName : a.SoapAttribute.AttributeName);
650 attribute.Namespace = a.SoapAttribute == null || a.SoapAttribute.Namespace == null ? ns : a.SoapAttribute.Namespace;
651 attribute.Form = XmlSchemaForm.Qualified; // attributes are always qualified since they're only used for encoded soap headers
652 attribute.Mapping = ImportTypeMapping(modelScope.GetTypeModel(accessorType), (a.SoapAttribute == null ? String.Empty : a.SoapAttribute.DataType), limiter);
653 attribute.Default = GetDefaultValue(model.FieldTypeDesc, a);
654 accessor.Attribute = attribute;
655 accessor.Elements = new ElementAccessor[0];
658 if ((flags & SoapAttributeFlags.Element) != flags)
659 throw new InvalidOperationException(Res.GetString(Res.XmlInvalidElementAttribute));
661 ElementAccessor element = new ElementAccessor();
662 element.IsSoap = true;
663 element.Name = XmlConvert.EncodeLocalName(a.SoapElement == null || a.SoapElement.ElementName.Length == 0 ? accessorName : a.SoapElement.ElementName);
664 element.Namespace = ns;
666 element.Mapping = ImportTypeMapping(modelScope.GetTypeModel(accessorType), (a.SoapElement == null ? String.Empty : a.SoapElement.DataType), limiter);
667 if (a.SoapElement != null)
668 element.IsNullable = a.SoapElement.IsNullable;
669 accessor.Elements = new ElementAccessor[] { element };
673 static ElementAccessor CreateElementAccessor(TypeMapping mapping, string ns) {
674 ElementAccessor element = new ElementAccessor();
675 element.IsSoap = true;
676 element.Name = mapping.TypeName; //XmlConvert.EncodeLocalName(name == null || name.Length == 0 ? mapping.TypeName : name);
677 element.Namespace = ns;
678 element.Mapping = mapping;
682 object GetDefaultValue(TypeDesc fieldTypeDesc, SoapAttributes a) {
683 if (a.SoapDefaultValue == null || a.SoapDefaultValue == DBNull.Value) return null;
684 if (!(fieldTypeDesc.Kind == TypeKind.Primitive || fieldTypeDesc.Kind == TypeKind.Enum)) {
685 a.SoapDefaultValue = null;
686 return a.SoapDefaultValue;
688 // for enums validate and return a string representation
689 if (fieldTypeDesc.Kind == TypeKind.Enum) {
690 if (fieldTypeDesc != typeScope.GetTypeDesc(a.SoapDefaultValue.GetType()))
691 throw new InvalidOperationException(Res.GetString(Res.XmlInvalidDefaultEnumValue, a.SoapDefaultValue.GetType().FullName, fieldTypeDesc.FullName));
692 string strValue = Enum.Format(a.SoapDefaultValue.GetType(), a.SoapDefaultValue, "G").Replace(",", " ");
693 string numValue = Enum.Format(a.SoapDefaultValue.GetType(), a.SoapDefaultValue, "D");
694 if (strValue == numValue) // means enum value wasn't recognized
695 throw new InvalidOperationException(Res.GetString(Res.XmlInvalidDefaultValue, strValue, a.SoapDefaultValue.GetType().FullName));
698 return a.SoapDefaultValue;
701 internal string XsdTypeName(Type type) {
702 if (type == typeof(object)) return Soap.UrType;
703 TypeDesc typeDesc = typeScope.GetTypeDesc(type);
704 if (typeDesc.IsPrimitive && typeDesc.DataType != null && typeDesc.DataType.Name != null && typeDesc.DataType.Name.Length > 0)
705 return typeDesc.DataType.Name;
706 return XsdTypeName(type, GetAttributes(type), typeDesc.Name);
708 internal string XsdTypeName(Type type, SoapAttributes a, string name) {
709 string typeName = name;
710 if (a.SoapType != null && a.SoapType.TypeName.Length > 0)
711 typeName = a.SoapType.TypeName;
713 if (type.IsGenericType && typeName.IndexOf('{') >= 0) {
714 Type genType = type.GetGenericTypeDefinition();
715 Type[] names = genType.GetGenericArguments();
716 Type[] types = type.GetGenericArguments();
718 for (int i = 0; i < names.Length; i++) {
719 string argument = "{" + names[i] + "}";
720 if (typeName.Contains(argument)) {
721 typeName = typeName.Replace(argument, XsdTypeName(types[i]));
722 if (typeName.IndexOf('{') < 0) {