Initial commit
[mono.git] / mcs / class / referencesource / System.Xml / System / Xml / Serialization / SoapReflectionImporter.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="SoapReflectionImporter.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">[....]</owner>                                                                
6 //------------------------------------------------------------------------------
7
8 namespace System.Xml.Serialization {
9
10     using System.Reflection;
11     using System;
12     using System.Globalization;
13     using System.Xml.Schema;
14     using System.Collections;
15     using System.ComponentModel;
16     using System.Threading;
17
18     /// <include file='doc\SoapReflectionImporter.uex' path='docs/doc[@for="SoapReflectionImporter"]/*' />
19     /// <devdoc>
20     ///    <para>[To be supplied.]</para>
21     /// </devdoc>
22     public class SoapReflectionImporter {
23         TypeScope typeScope;
24         SoapAttributeOverrides attributeOverrides;
25         NameTable types = new NameTable();      // xmltypename + xmlns -> Mapping
26         NameTable nullables = new NameTable();  // xmltypename + xmlns -> NullableMapping
27         StructMapping root;
28         string defaultNs;
29         ModelScope modelScope;
30
31        
32         /// <include file='doc\SoapReflectionImporter.uex' path='docs/doc[@for="SoapReflectionImporter.SoapReflectionImporter"]/*' />
33         /// <devdoc>
34         ///    <para>[To be supplied.]</para>
35         /// </devdoc>
36         public SoapReflectionImporter() : this(null, null) {
37         }
38
39         /// <include file='doc\SoapReflectionImporter.uex' path='docs/doc[@for="SoapReflectionImporter.SoapReflectionImporter1"]/*' />
40         /// <devdoc>
41         ///    <para>[To be supplied.]</para>
42         /// </devdoc>
43         public SoapReflectionImporter(string defaultNamespace) : this(null, defaultNamespace) {
44         }
45
46         /// <include file='doc\SoapReflectionImporter.uex' path='docs/doc[@for="SoapReflectionImporter.SoapReflectionImporter2"]/*' />
47         /// <devdoc>
48         ///    <para>[To be supplied.]</para>
49         /// </devdoc>
50         public SoapReflectionImporter(SoapAttributeOverrides attributeOverrides) : this(attributeOverrides, null) {
51         }
52
53         /// <include file='doc\SoapReflectionImporter.uex' path='docs/doc[@for="SoapReflectionImporter.SoapReflectionImporter3"]/*' />
54         /// <devdoc>
55         ///    <para>[To be supplied.]</para>
56         /// </devdoc>
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);
66         }
67
68         /// <include file='doc\SoapReflectionImporter.uex' path='docs/doc[@for="SoapReflectionImporter.IncludeTypes"]/*' />
69         /// <devdoc>
70         ///    <para>[To be supplied.]</para>
71         /// </devdoc>
72         public void IncludeTypes(ICustomAttributeProvider provider) {
73             IncludeTypes(provider, new RecursionLimiter());
74         }
75
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);
80             }
81         }
82
83         /// <include file='doc\SoapReflectionImporter.uex' path='docs/doc[@for="SoapReflectionImporter.IncludeType"]/*' />
84         /// <devdoc>
85         ///    <para>[To be supplied.]</para>
86         /// </devdoc>
87         public void IncludeType(Type type) {
88             IncludeType(type, new RecursionLimiter());
89         }
90
91         void IncludeType(Type type, RecursionLimiter limiter) {
92             ImportTypeMapping(modelScope.GetTypeModel(type), limiter);
93         }
94
95         /// <include file='doc\SoapReflectionImporter.uex' path='docs/doc[@for="XmlReflectionImporter.ImportTypeMapping"]/*' />
96         /// <devdoc>
97         ///    <para>[To be supplied.]</para>
98         /// </devdoc>
99         public XmlTypeMapping ImportTypeMapping(Type type) {
100             return ImportTypeMapping(type, null);
101         }
102
103         /// <include file='doc\SoapReflectionImporter.uex' path='docs/doc[@for="XmlReflectionImporter.ImportTypeMapping1"]/*' />
104         /// <devdoc>
105         ///    <para>[To be supplied.]</para>
106         /// </devdoc>
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;
118             return xmlMapping;
119         }
120
121         /// <include file='doc\SoapReflectionImporter.uex' path='docs/doc[@for="SoapReflectionImporter.ImportMembersMapping"]/*' />
122         /// <devdoc>
123         ///    <para>[To be supplied.]</para>
124         /// </devdoc>
125         public XmlMembersMapping ImportMembersMapping(string elementName, string ns, XmlReflectionMember[] members) {
126             return ImportMembersMapping(elementName, ns, members, true, true, false);
127         }
128
129         /// <include file='doc\SoapReflectionImporter.uex' path='docs/doc[@for="SoapReflectionImporter.ImportMembersMapping1"]/*' />
130         /// <devdoc>
131         ///    <para>[To be supplied.]</para>
132         /// </devdoc>
133         public XmlMembersMapping ImportMembersMapping(string elementName, string ns, XmlReflectionMember[] members, bool hasWrapperElement, bool writeAccessors) {
134             return ImportMembersMapping(elementName, ns, members, hasWrapperElement, writeAccessors, false);
135         }
136
137         /// <include file='doc\SoapReflectionImporter.uex' path='docs/doc[@for="SoapReflectionImporter.ImportMembersMapping2"]/*' />
138         /// <devdoc>
139         ///    <para>[To be supplied.]</para>
140         /// </devdoc>
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);
143         }
144         /// <include file='doc\SoapReflectionImporter.uex' path='docs/doc[@for="SoapReflectionImporter.ImportMembersMapping3"]/*' />
145         /// <devdoc>
146         ///    <para>[To be supplied.]</para>
147         /// </devdoc>
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);
152
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;
160             return xmlMapping;
161         }
162
163         Exception ReflectionException(string context, Exception e) {
164             return new InvalidOperationException(Res.GetString(Res.XmlReflectionError, context), e);
165         }
166
167         SoapAttributes GetAttributes(Type type) {
168             SoapAttributes attrs = attributeOverrides[type];
169             if (attrs != null) return attrs;
170             return new SoapAttributes(type);
171         }
172
173         SoapAttributes GetAttributes(MemberInfo memberInfo) {
174             SoapAttributes attrs = attributeOverrides[memberInfo.DeclaringType, memberInfo.Name];
175             if (attrs != null) return attrs;
176             return new SoapAttributes(memberInfo);
177         }
178
179         TypeMapping ImportTypeMapping(TypeModel model, RecursionLimiter limiter) {
180             return ImportTypeMapping(model, String.Empty, limiter);
181         }
182
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"));
187                 }
188                 TypeDesc td = typeScope.GetTypeDesc(dataType, XmlSchema.Namespace);
189                 if (td == null) {
190                     throw new InvalidOperationException(Res.GetString(Res.XmlInvalidXsdDataType, dataType, "SoapElementAttribute.DataType", new XmlQualifiedName(dataType, XmlSchema.Namespace).ToString()));
191                 }
192                 if (model.TypeDesc.FullName != td.FullName) {
193                     throw new InvalidOperationException(Res.GetString(Res.XmlDataTypeMismatch, dataType, "SoapElementAttribute.DataType", model.TypeDesc.FullName));
194                 }
195             }
196
197             SoapAttributes a = GetAttributes(model.Type);
198             
199             if ((a.SoapFlags & ~SoapAttributeFlags.Type) != 0)
200                 throw new InvalidOperationException(Res.GetString(Res.XmlInvalidTypeAttributes, model.Type.FullName));
201
202             switch (model.TypeDesc.Kind) {
203                 case TypeKind.Enum: 
204                     return ImportEnumMapping((EnumModel)model);
205                 case TypeKind.Primitive:
206                     return ImportPrimitiveMapping((PrimitiveModel)model, dataType);
207                 case TypeKind.Array:
208                 case TypeKind.Collection:
209                 case TypeKind.Enumerable:
210                     return ImportArrayLikeMapping((ArrayModel)model, limiter);
211                 case TypeKind.Root:
212                 case TypeKind.Class:
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);
226                     }
227                     else {
228                         return ImportStructLikeMapping((StructModel)model, limiter);
229                     }
230                 default:
231                     throw new NotSupportedException(Res.GetString(Res.XmlUnsupportedSoapTypeKind, model.TypeDesc.FullName));
232             }
233         }
234
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;
244             return mapping;
245         }
246         
247         StructMapping GetRootMapping() {
248             if (root == null) {
249                 root = CreateRootMapping();
250                 typeScope.AddTypeMapping(root);
251             }
252             return root;
253         }
254
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));
260             return mapping;
261         }
262
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)
271                         return mapping;
272                     else if (mapping.BaseMapping == baseMapping) {
273                         return mapping;
274                     }
275                     else {
276                         throw new InvalidOperationException(Res.GetString(Res.XmlTypesDuplicate, typeDesc.FullName, existingMapping.TypeDesc.FullName, typeDesc.Name, existingMapping.Namespace));
277                     }
278                 }
279                 else if (!(baseMapping is PrimitiveMapping)){
280                     throw new InvalidOperationException(Res.GetString(Res.XmlTypesDuplicate, typeDesc.FullName, existingMapping.TypeDesc.FullName, typeDesc.Name, existingMapping.Namespace));
281                 }
282             }
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);
291             return mapping;
292         }
293
294         StructMapping ImportStructLikeMapping(StructModel model, RecursionLimiter limiter) {
295             if (model.TypeDesc.Kind == TypeKind.Root) return GetRootMapping();
296
297             SoapAttributes a = GetAttributes(model.Type);
298
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);
304
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));
317             return mapping;
318         }
319
320                 limiter.Depth++;
321
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)) {
327                         //
328                         // if InitializeStructMembers returns true, then there were *no* chages to the DeferredWorkItems
329                         //
330 #if DEBUG
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"));
336 #endif
337                         // Remove the last work item
338                         limiter.DeferredWorkItems.RemoveAt(index);
339                     }
340                 }
341                 limiter.Depth--;
342             }
343             return mapping;
344         }
345
346
347         bool InitializeStructMembers(StructMapping mapping, StructModel model, RecursionLimiter limiter) {
348             if (mapping.IsFullyInitialized)
349                 return true;
350             if (model.TypeDesc.BaseTypeDesc != null) {
351                 StructMapping baseMapping = ImportStructLikeMapping((StructModel)modelScope.GetTypeModel(model.Type.BaseType, false), limiter);
352
353                 // check to see if the import of the baseMapping was deffered
354                 int baseIndex = limiter.DeferredWorkItems.IndexOf(mapping.BaseMapping);
355                 if (baseIndex < 0) {
356                     mapping.BaseMapping = baseMapping;
357                 }
358                 else {
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));
362                     }
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;
369                     }
370                     return false;
371                 }                
372                 }
373                 ArrayList members = new ArrayList();
374                 foreach (MemberInfo memberInfo in model.GetMemberInfos()) {
375                     if ((memberInfo.MemberType & (MemberTypes.Field | MemberTypes.Property)) == 0)
376                         continue;
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;
383
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));
389                     }
390                     if (mapping.BaseMapping != null) {
391                         if (mapping.BaseMapping.Declares(member, mapping.TypeName)) continue;
392                     }
393                     members.Add(member);
394                 }
395                 mapping.Members = (MemberMapping[])members.ToArray(typeof(MemberMapping));
396                 if (mapping.BaseMapping == null) mapping.BaseMapping = GetRootMapping();
397                  IncludeTypes(model.Type, limiter);
398
399             return true;
400         }
401
402
403         ArrayMapping ImportArrayLikeMapping(ArrayModel model, RecursionLimiter limiter) {
404
405             ArrayMapping mapping = new ArrayMapping();
406             mapping.IsSoap = true;
407             TypeMapping itemTypeMapping = ImportTypeMapping(model.Element, limiter);
408
409             if (itemTypeMapping.TypeDesc.IsValueType && !itemTypeMapping.TypeDesc.IsPrimitive && !itemTypeMapping.TypeDesc.IsEnum)
410                 throw new NotSupportedException(Res.GetString(Res.XmlRpcArrayOfValueTypes, model.TypeDesc.FullName));
411             
412             mapping.TypeDesc = model.TypeDesc;
413             mapping.Elements = new ElementAccessor[] { 
414                 CreateElementAccessor(itemTypeMapping, mapping.Namespace) };
415             SetArrayMappingType(mapping);
416
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;
427                 }
428                 mapping.Next = first;
429                 types[mapping.TypeName, mapping.Namespace] = mapping;
430                 return mapping;
431             }
432             typeScope.AddTypeMapping(mapping);
433             types.Add(mapping.TypeName, mapping.Namespace, mapping);
434             IncludeTypes(model.Type);
435             return mapping;
436         }
437
438         // 
439         void SetArrayMappingType(ArrayMapping mapping) {
440             bool useDefaultNs = false;
441
442             string itemTypeName;
443             string itemTypeNamespace;
444
445             TypeMapping itemTypeMapping;
446             if (mapping.Elements.Length == 1)
447                 itemTypeMapping = mapping.Elements[0].Mapping;
448             else
449                 itemTypeMapping = null;
450
451             if (itemTypeMapping is EnumMapping) {
452                 itemTypeNamespace = itemTypeMapping.Namespace;
453                 itemTypeName = itemTypeMapping.TypeName;
454             }
455             else if (itemTypeMapping is PrimitiveMapping) {
456                 itemTypeNamespace = itemTypeMapping.TypeDesc.IsXsdType ? XmlSchema.Namespace : UrtTypes.Namespace;
457                 itemTypeName = itemTypeMapping.TypeDesc.DataType.Name;
458                 useDefaultNs = true;
459             }
460             else if (itemTypeMapping is StructMapping) {
461                 if (itemTypeMapping.TypeDesc.IsRoot) {
462                     itemTypeNamespace = XmlSchema.Namespace;
463                     itemTypeName = Soap.UrType;
464                     useDefaultNs = true;
465                 }
466                 else {
467                     itemTypeNamespace = itemTypeMapping.Namespace;
468                     itemTypeName = itemTypeMapping.TypeName;
469                 }
470             }
471             else if (itemTypeMapping is ArrayMapping) {
472                 itemTypeNamespace = itemTypeMapping.Namespace;
473                 itemTypeName = itemTypeMapping.TypeName;
474             }
475             else {
476                 throw new InvalidOperationException(Res.GetString(Res.XmlInvalidSoapArray, mapping.TypeDesc.FullName));
477             }
478
479             itemTypeName = CodeIdentifier.MakePascal(itemTypeName);
480             string uniqueName = "ArrayOf" + itemTypeName;
481             string ns = useDefaultNs ? defaultNs : itemTypeNamespace;
482             int i = 1;
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)) {
488                         break;
489                     }
490                 }
491                 // need to re-name the mapping
492                 uniqueName = itemTypeName + i.ToString(CultureInfo.InvariantCulture);
493                 existingMapping = (TypeMapping)types[uniqueName, ns];
494                 i++;
495             }
496             mapping.Namespace = ns;
497             mapping.TypeName = uniqueName;
498         }
499
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));
510                     }
511                 }
512             }
513             else {
514                 mapping.TypeDesc = model.TypeDesc;
515             }
516             mapping.TypeName = mapping.TypeDesc.DataType.Name;
517             mapping.Namespace = mapping.TypeDesc.IsXsdType ? XmlSchema.Namespace : UrtTypes.Namespace;
518             return mapping;
519         }
520        
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);
528
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);
543                 }
544                 if (constants.Count == 0) {
545                     throw new InvalidOperationException(Res.GetString(Res.XmlNoSerializableMembers, model.TypeDesc.FullName));
546                 }
547                 mapping.Constants = (ConstantMapping[])constants.ToArray(typeof(ConstantMapping));
548             }
549             return mapping;
550         }
551         
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();
559
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;
564             return constant;
565         }
566         
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++) {
572                 try {
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;
578                     }
579                     mappings[i] = mapping;
580                 }
581                 catch (Exception e) {
582                     if (e is ThreadAbortException || e is StackOverflowException || e is OutOfMemoryException) {
583                         throw;
584                     }
585                     throw ReflectionException(xmlReflectionMembers[i].MemberName, e);
586                 }
587             }
588             members.Members = mappings;
589             members.HasWrapperElement = hasWrapperElement;
590             if (hasWrapperElement) {
591                 members.ValidateRpcWrapperElement = validateWrapperElement;
592             }
593             members.WriteAccessors = writeAccessors;
594             members.IsSoap = true;
595             if (hasWrapperElement && !writeAccessors)
596                 members.Namespace = ns;
597             return members;
598         }
599         
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;
614             return member;
615         }
616
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);
629             return member;
630         }
631
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));
638             }
639
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));
644
645                 if ((flags & SoapAttributeFlags.Attribute) != flags)
646                     throw new InvalidOperationException(Res.GetString(Res.XmlInvalidElementAttribute));
647                 
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];
656             }
657             else {
658                 if ((flags & SoapAttributeFlags.Element) != flags)
659                     throw new InvalidOperationException(Res.GetString(Res.XmlInvalidElementAttribute));
660
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;
665                 element.Form = form;
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 };
670             }
671         }
672
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;
679             return element;
680         }
681
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;
687             }
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));
696                 return strValue;
697             }
698             return a.SoapDefaultValue;
699         }
700
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);
707         }
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;
712
713             if (type.IsGenericType && typeName.IndexOf('{') >= 0) {
714                 Type genType = type.GetGenericTypeDefinition();
715                 Type[] names = genType.GetGenericArguments();
716                 Type[] types = type.GetGenericArguments();
717
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) {
723                             break;
724                         }
725                     }
726                 }
727             }
728             // 
729             return typeName;
730         }
731     }
732 }