Update Reference Sources to .NET Framework 4.6
[mono.git] / mcs / class / referencesource / System.Xml / System / Xml / Serialization / XmlCodeExporter.cs
1 //------------------------------------------------------------------------------
2 // <copyright file="XmlCodeExporter.cs" company="Microsoft">
3 //     Copyright (c) Microsoft Corporation.  All rights reserved.
4 // </copyright>
5 // <owner current="true" primary="true">Microsoft</owner>                                                                
6 //------------------------------------------------------------------------------
7
8 namespace System.Xml.Serialization {
9     
10     using System;
11     using System.Collections;
12     using System.IO;
13     using System.ComponentModel;
14     using System.Xml.Schema;
15     using System.CodeDom;
16     using System.CodeDom.Compiler;
17     using System.Reflection;
18     using System.Globalization;
19     using System.Diagnostics;
20     using System.Security.Permissions;
21     using System.Xml.Serialization.Advanced;
22
23     /// <include file='doc\XmlCodeExporter.uex' path='docs/doc[@for="XmlCodeExporter"]/*' />
24     ///<internalonly/>
25     /// <devdoc>
26     ///    <para>[To be supplied.]</para>
27     /// </devdoc>
28     public class XmlCodeExporter : CodeExporter {
29         /// <include file='doc\XmlCodeExporter.uex' path='docs/doc[@for="XmlCodeExporter.XmlCodeExporter"]/*' />
30         /// <devdoc>
31         ///    <para>[To be supplied.]</para>
32         /// </devdoc>
33         public XmlCodeExporter(CodeNamespace codeNamespace) : base(codeNamespace, null, null, CodeGenerationOptions.GenerateProperties, null) {}
34
35         /// <include file='doc\XmlCodeExporter.uex' path='docs/doc[@for="XmlCodeExporter.XmlCodeExporter1"]/*' />
36         /// <devdoc>
37         ///    <para>[To be supplied.]</para>
38         /// </devdoc>
39         public XmlCodeExporter(CodeNamespace codeNamespace, CodeCompileUnit codeCompileUnit) : base(codeNamespace, codeCompileUnit, null, CodeGenerationOptions.GenerateProperties, null) {}
40
41         /// <include file='doc\XmlCodeExporter.uex' path='docs/doc[@for="XmlCodeExporter.XmlCodeExporter2"]/*' />
42         /// <devdoc>
43         ///    <para>[To be supplied.]</para>
44         /// </devdoc>
45         public XmlCodeExporter(CodeNamespace codeNamespace, CodeCompileUnit codeCompileUnit, CodeGenerationOptions options) 
46             : base(codeNamespace, codeCompileUnit, null, options, null) {}
47
48         /// <include file='doc\XmlCodeExporter.uex' path='docs/doc[@for="XmlCodeExporter.XmlCodeExporter3"]/*' />
49         /// <devdoc>
50         ///    <para>[To be supplied.]</para>
51         /// </devdoc>
52         public XmlCodeExporter(CodeNamespace codeNamespace, CodeCompileUnit codeCompileUnit, CodeGenerationOptions options, Hashtable mappings) 
53             : base(codeNamespace, codeCompileUnit, null, options, mappings) {}
54
55         /// <include file='doc\XmlCodeExporter.uex' path='docs/doc[@for="XmlCodeExporter.XmlCodeExporter4"]/*' />
56         /// <devdoc>
57         ///    <para>[To be supplied.]</para>
58         /// </devdoc>
59         public XmlCodeExporter(CodeNamespace codeNamespace, CodeCompileUnit codeCompileUnit, CodeDomProvider codeProvider, CodeGenerationOptions options, Hashtable mappings) 
60             : base(codeNamespace, codeCompileUnit, codeProvider, options, mappings) {}
61
62         /// <include file='doc\XmlCodeExporter.uex' path='docs/doc[@for="XmlCodeExporter.ExportTypeMapping"]/*' />
63         /// <devdoc>
64         ///    <para>[To be supplied.]</para>
65         /// </devdoc>
66         public void ExportTypeMapping(XmlTypeMapping xmlTypeMapping) {
67             xmlTypeMapping.CheckShallow();
68             CheckScope(xmlTypeMapping.Scope);
69             if (xmlTypeMapping.Accessor.Any) throw new InvalidOperationException(Res.GetString(Res.XmlIllegalWildcard));
70
71             ExportElement(xmlTypeMapping.Accessor);
72         }
73
74         /// <include file='doc\XmlCodeExporter.uex' path='docs/doc[@for="XmlCodeExporter.ExportMembersMapping"]/*' />
75         /// <devdoc>
76         ///    <para>[To be supplied.]</para>
77         /// </devdoc>
78         public void ExportMembersMapping(XmlMembersMapping xmlMembersMapping) {
79             xmlMembersMapping.CheckShallow();
80             CheckScope(xmlMembersMapping.Scope);
81
82             for (int i = 0; i < xmlMembersMapping.Count; i++) {
83                 AccessorMapping mapping = xmlMembersMapping[i].Mapping;
84                 if (mapping.Xmlns == null) {
85                     if (mapping.Attribute != null) {
86                         ExportType(mapping.Attribute.Mapping, Accessor.UnescapeName(mapping.Attribute.Name), mapping.Attribute.Namespace, null, false);
87                     }
88                     if (mapping.Elements != null) {
89                         for (int j = 0; j < mapping.Elements.Length; j++) {
90                             ElementAccessor element = mapping.Elements[j];
91                             ExportType(element.Mapping, Accessor.UnescapeName(element.Name), element.Namespace, null, false);
92                         }
93                     }
94                     if (mapping.Text != null) {
95                         ExportType(mapping.Text.Mapping, Accessor.UnescapeName(mapping.Text.Name), mapping.Text.Namespace, null, false);
96                     }
97                 }
98             }
99         }
100
101         void ExportElement(ElementAccessor element) {
102             ExportType(element.Mapping, Accessor.UnescapeName(element.Name), element.Namespace, element, true);
103         }
104
105         void ExportType(TypeMapping mapping, string ns) {
106             ExportType(mapping, null, ns, null, true);
107         }
108
109         void ExportType(TypeMapping mapping, string name, string ns, ElementAccessor rootElement, bool checkReference) {
110             if (mapping.IsReference && mapping.Namespace != Soap.Encoding)
111                 return;
112
113             if (mapping is StructMapping && checkReference && ((StructMapping)mapping).ReferencedByTopLevelElement && rootElement == null)
114                 return;
115
116             if (mapping is ArrayMapping && rootElement != null && rootElement.IsTopLevelInSchema && ((ArrayMapping)mapping).TopLevelMapping != null) {
117                 mapping = ((ArrayMapping)mapping).TopLevelMapping;
118             }
119
120             CodeTypeDeclaration codeClass = null;
121
122             if (ExportedMappings[mapping] == null) {
123                 ExportedMappings.Add(mapping, mapping);
124                 if (mapping.TypeDesc.IsMappedType) {
125                     codeClass = mapping.TypeDesc.ExtendedType.ExportTypeDefinition(CodeNamespace, CodeCompileUnit);
126                 }
127                 else if (mapping is EnumMapping) {
128                     codeClass = ExportEnum((EnumMapping)mapping, typeof(XmlEnumAttribute));
129                 }
130                 else if (mapping is StructMapping) {
131                     codeClass = ExportStruct((StructMapping)mapping);
132                 }
133                 else if (mapping is ArrayMapping) {
134                     EnsureTypesExported(((ArrayMapping)mapping).Elements, ns);
135                 }
136                 if (codeClass != null) {
137                     if (!mapping.TypeDesc.IsMappedType) {
138                         // Add [GeneratedCodeAttribute(Tool=.., Version=..)]
139                         codeClass.CustomAttributes.Add(GeneratedCodeAttribute);
140
141                         // Add [SerializableAttribute]
142                         codeClass.CustomAttributes.Add(new CodeAttributeDeclaration(typeof(SerializableAttribute).FullName));
143
144                         if (!codeClass.IsEnum) {
145                             // Add [DebuggerStepThrough]
146                             codeClass.CustomAttributes.Add(new CodeAttributeDeclaration(typeof(DebuggerStepThroughAttribute).FullName));
147                             // Add [DesignerCategory("code")]
148                             codeClass.CustomAttributes.Add(new CodeAttributeDeclaration(typeof(DesignerCategoryAttribute).FullName, new CodeAttributeArgument[] {new CodeAttributeArgument(new CodePrimitiveExpression("code"))}));
149                         }
150                         AddTypeMetadata(codeClass.CustomAttributes, typeof(XmlTypeAttribute), mapping.TypeDesc.Name, Accessor.UnescapeName(mapping.TypeName), mapping.Namespace, mapping.IncludeInSchema);
151                     }
152                     else if (FindAttributeDeclaration(typeof(GeneratedCodeAttribute), codeClass.CustomAttributes) == null) {
153                         // Add [GeneratedCodeAttribute(Tool=.., Version=..)]
154                         codeClass.CustomAttributes.Add(GeneratedCodeAttribute);
155                     }
156                     ExportedClasses.Add(mapping, codeClass);
157                 }
158             }
159             else
160                 codeClass = (CodeTypeDeclaration)ExportedClasses[mapping];
161             
162             if (codeClass != null && rootElement != null) 
163                 AddRootMetadata(codeClass.CustomAttributes, mapping, name, ns, rootElement);
164         }
165
166         void AddRootMetadata(CodeAttributeDeclarationCollection metadata, TypeMapping typeMapping, string name, string ns, ElementAccessor rootElement) {
167             string rootAttrName = typeof(XmlRootAttribute).FullName;
168             
169             // check that we haven't already added a root attribute since we can only add one
170             foreach (CodeAttributeDeclaration attr in metadata) {
171                 if (attr.Name == rootAttrName) return;
172             }
173
174             CodeAttributeDeclaration attribute = new CodeAttributeDeclaration(rootAttrName);
175             if (typeMapping.TypeDesc.Name != name) {
176                 attribute.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression(name)));
177             }
178             if (ns != null) {
179                 attribute.Arguments.Add(new CodeAttributeArgument("Namespace", new CodePrimitiveExpression(ns)));
180             }
181             if (typeMapping.TypeDesc != null && typeMapping.TypeDesc.IsAmbiguousDataType) {
182                 attribute.Arguments.Add(new CodeAttributeArgument("DataType", new CodePrimitiveExpression(typeMapping.TypeDesc.DataType.Name)));
183             }
184             if ((object)(rootElement.IsNullable) != null) {
185                 attribute.Arguments.Add(new CodeAttributeArgument("IsNullable", new CodePrimitiveExpression((bool)rootElement.IsNullable)));
186             }
187             metadata.Add(attribute);
188         }
189
190         CodeAttributeArgument[] GetDefaultValueArguments(PrimitiveMapping mapping, object value, out CodeExpression initExpression) {
191             initExpression = null;
192             if (value == null) return null;
193
194             CodeExpression valueExpression = null;
195             CodeExpression typeofValue = null;
196             Type type = value.GetType();
197             CodeAttributeArgument[] arguments = null;
198
199             if (mapping is EnumMapping) {
200                 #if DEBUG
201                     // use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe
202                     if (value.GetType() != typeof(string)) throw new InvalidOperationException(Res.GetString(Res.XmlInternalErrorDetails, "Invalid enumeration type " + value.GetType().Name));
203                 #endif
204
205                 if (((EnumMapping)mapping).IsFlags) {
206                     string[] values = ((string)value).Split(null);
207                     for (int i = 0; i < values.Length; i++) {
208                         if (values[i].Length == 0) continue;
209                         CodeExpression enumRef = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(mapping.TypeDesc.FullName), values[i]);
210                         if (valueExpression != null)
211                             valueExpression = new CodeBinaryOperatorExpression(valueExpression, CodeBinaryOperatorType.BitwiseOr, enumRef);
212                         else
213                             valueExpression = enumRef;
214                     }
215                 }
216                 else {
217                     valueExpression = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(mapping.TypeDesc.FullName), (string)value);
218                 }
219                 initExpression = valueExpression;
220                 arguments  = new CodeAttributeArgument[] {new CodeAttributeArgument(valueExpression)};
221             }
222             else if (type == typeof(bool) ||
223                 type == typeof(Int32)     ||
224                 type == typeof(string)    ||
225                 type == typeof(double)) {
226
227                 initExpression = valueExpression = new CodePrimitiveExpression(value);
228                 arguments = new CodeAttributeArgument[] {new CodeAttributeArgument(valueExpression)};
229             }
230             else if (type == typeof(Int16) ||
231                 type == typeof(Int64)      ||
232                 type == typeof(float)      ||
233                 type == typeof(byte)       ||
234                 type == typeof(decimal)) {
235                 valueExpression = new CodePrimitiveExpression(Convert.ToString(value, NumberFormatInfo.InvariantInfo));
236                 typeofValue = new CodeTypeOfExpression(type.FullName);
237                 arguments  = new CodeAttributeArgument[] {new CodeAttributeArgument(typeofValue), new CodeAttributeArgument(valueExpression)};
238                 initExpression = new CodeCastExpression(type.FullName, new CodePrimitiveExpression(value));
239             }
240             else if (type == typeof(sbyte) ||
241                 type == typeof(UInt16)     ||
242                 type == typeof(UInt32)     ||
243                 type == typeof(UInt64)) {
244                 // need to promote the non-CLS complient types
245
246                 value = PromoteType(type, value);
247
248                 valueExpression = new CodePrimitiveExpression(Convert.ToString(value, NumberFormatInfo.InvariantInfo));
249                 typeofValue = new CodeTypeOfExpression(type.FullName);
250                 arguments  = new CodeAttributeArgument[] {new CodeAttributeArgument(typeofValue), new CodeAttributeArgument(valueExpression)};
251                 initExpression = new CodeCastExpression(type.FullName, new CodePrimitiveExpression(value));
252             }
253             else if (type == typeof(DateTime)) {
254                 DateTime dt = (DateTime)value;
255                 string dtString;
256                 long ticks;
257                 if (mapping.TypeDesc.FormatterName == "Date") {
258                     dtString = XmlCustomFormatter.FromDate(dt);
259                     ticks = (new DateTime(dt.Year, dt.Month, dt.Day)).Ticks;
260                 }
261                 else if (mapping.TypeDesc.FormatterName == "Time") {
262                     dtString = XmlCustomFormatter.FromDateTime(dt);
263                     ticks = dt.Ticks;
264                 }
265                 else {
266                     dtString = XmlCustomFormatter.FromDateTime(dt);
267                     ticks = dt.Ticks;
268                 }
269                 valueExpression = new CodePrimitiveExpression(dtString);
270                 typeofValue = new CodeTypeOfExpression(type.FullName);
271                 arguments  = new CodeAttributeArgument[] {new CodeAttributeArgument(typeofValue), new CodeAttributeArgument(valueExpression)};
272                 initExpression = new CodeObjectCreateExpression(new CodeTypeReference(typeof(DateTime)), new CodeExpression[] {new CodePrimitiveExpression(ticks)});
273             }
274             else if (type == typeof(Guid)) {
275                 valueExpression = new CodePrimitiveExpression(Convert.ToString(value, NumberFormatInfo.InvariantInfo));
276                 typeofValue = new CodeTypeOfExpression(type.FullName);
277                 arguments  = new CodeAttributeArgument[] {new CodeAttributeArgument(typeofValue), new CodeAttributeArgument(valueExpression)};
278                 initExpression = new CodeObjectCreateExpression(new CodeTypeReference(typeof(Guid)), new CodeExpression[] {valueExpression});
279
280             }
281             if (mapping.TypeDesc.FullName != type.ToString() && !(mapping is EnumMapping)) {
282                 // generate cast
283                 initExpression = new CodeCastExpression(mapping.TypeDesc.FullName, initExpression);
284             }
285             return arguments;
286         }
287
288         object ImportDefault(TypeMapping mapping, string defaultValue) {
289             if (defaultValue == null)
290                 return null;
291
292             if (mapping.IsList) {
293                 string[] vals = defaultValue.Trim().Split(null);
294
295                 // count all non-zero length values;
296                 int count = 0;
297                 for (int i = 0; i < vals.Length; i++) {
298                     if (vals[i] != null && vals[i].Length > 0) count++;
299                 }
300
301                 object[] values = new object[count];
302                 count = 0;
303                 for (int i = 0; i < vals.Length; i++) {
304                     if (vals[i] != null && vals[i].Length > 0) {
305                         values[count++] = ImportDefaultValue(mapping, vals[i]);
306                     }
307                 }
308                 return values;
309             }
310             return  ImportDefaultValue(mapping, defaultValue);
311         }
312
313         object ImportDefaultValue(TypeMapping mapping, string defaultValue) {
314             if (defaultValue == null)
315                 return null;
316             if (!(mapping is PrimitiveMapping))
317                 return DBNull.Value;
318
319             if (mapping is EnumMapping) {
320                 EnumMapping em = (EnumMapping)mapping;
321                 ConstantMapping[] c = em.Constants;
322
323                 if (em.IsFlags) {
324                     Hashtable values = new Hashtable();
325                     string[] names = new string[c.Length];
326                     long[] ids = new long[c.Length];
327
328                     for (int i = 0; i < c.Length; i++) {
329                         ids[i] = em.IsFlags ? 1L << i : (long)i;
330                         names[i] = c[i].Name;
331                         values.Add(c[i].Name, ids[i]);
332                     }
333                     // this validates the values
334                     long val = XmlCustomFormatter.ToEnum(defaultValue, values, em.TypeName, true);
335                     return XmlCustomFormatter.FromEnum(val, names, ids, em.TypeDesc.FullName);
336                 }
337                 else {
338                     for (int i = 0; i < c.Length; i++) {
339                         if (c[i].XmlName == defaultValue)
340                             return c[i].Name;
341                     }
342                 }
343                 throw new InvalidOperationException(Res.GetString(Res.XmlInvalidDefaultValue, defaultValue, em.TypeDesc.FullName));
344             }
345
346             // Primitive mapping
347             PrimitiveMapping pm = (PrimitiveMapping)mapping;
348
349             if (!pm.TypeDesc.HasCustomFormatter) {
350                 if (pm.TypeDesc.FormatterName == "String")
351                     return defaultValue;
352                 if (pm.TypeDesc.FormatterName == "DateTime")
353                     return XmlCustomFormatter.ToDateTime(defaultValue);
354
355                 Type formatter = typeof(XmlConvert);
356
357                 MethodInfo format = formatter.GetMethod("To" + pm.TypeDesc.FormatterName, new Type[] {typeof(string)});
358                 if (format != null) {
359                     return format.Invoke(formatter, new Object[] {defaultValue});
360                 }
361 #if DEBUG
362                 Debug.WriteLineIf(DiagnosticsSwitches.XmlSerialization.TraceVerbose, "XmlSerialization::Failed to GetMethod " + formatter.Name + ".To" + pm.TypeDesc.FormatterName);
363 #endif
364             }
365             else {
366                 if (pm.TypeDesc.HasDefaultSupport) {
367                     return XmlCustomFormatter.ToDefaultValue(defaultValue, pm.TypeDesc.FormatterName);
368                 }
369             }
370             return DBNull.Value;
371         }
372
373         void AddDefaultValueAttribute(CodeMemberField field, CodeAttributeDeclarationCollection metadata, object defaultValue, TypeMapping mapping, CodeCommentStatementCollection comments, TypeDesc memberTypeDesc, Accessor accessor, CodeConstructor ctor) {
374             string attributeName = accessor.IsFixed ? "fixed" : "default";
375             if (!memberTypeDesc.HasDefaultSupport) {
376                 if (comments != null && defaultValue is string) {
377                     DropDefaultAttribute(accessor, comments, memberTypeDesc.FullName);
378                     // do not generate intializers for the user prefered types if they do not have default capability
379                     AddWarningComment(comments, Res.GetString(Res.XmlDropAttributeValue, attributeName, mapping.TypeName, defaultValue.ToString()));
380                 }
381                 return;
382             }
383             if (memberTypeDesc.IsArrayLike && accessor is ElementAccessor) {
384                 if (comments != null && defaultValue is string) {
385                     DropDefaultAttribute(accessor, comments, memberTypeDesc.FullName);
386                     // do not generate intializers for array-like types
387                     AddWarningComment(comments, Res.GetString(Res.XmlDropArrayAttributeValue, attributeName, defaultValue.ToString(), ((ElementAccessor)accessor).Name));
388                 }
389                 return;
390             }
391             if (mapping.TypeDesc.IsMappedType && field != null && defaultValue is string) {
392                 SchemaImporterExtension extension = mapping.TypeDesc.ExtendedType.Extension;
393                 CodeExpression init = extension.ImportDefaultValue((string)defaultValue, mapping.TypeDesc.FullName);
394
395                 if (init != null) {
396                     if (ctor != null) {
397                         AddInitializationStatement(ctor, field, init);
398                     }
399                     else {
400                         field.InitExpression = extension.ImportDefaultValue((string)defaultValue, mapping.TypeDesc.FullName);
401                     }
402                 }
403                 if (comments != null) {
404                     DropDefaultAttribute(accessor, comments, mapping.TypeDesc.FullName);
405                     if (init == null) {
406                         AddWarningComment(comments, Res.GetString(Res.XmlNotKnownDefaultValue, extension.GetType().FullName, attributeName, (string)defaultValue, mapping.TypeName, mapping.Namespace));
407                     }
408                 }
409                 return;
410             }
411             object value = null;
412             if (defaultValue is string || defaultValue == null) {
413                 value = ImportDefault(mapping, (string)defaultValue);
414             }
415             if (value == null) return;
416             if (!(mapping is PrimitiveMapping)) {
417                 if (comments != null) { 
418                     DropDefaultAttribute(accessor, comments, memberTypeDesc.FullName);
419                     AddWarningComment(comments, Res.GetString(Res.XmlDropNonPrimitiveAttributeValue, attributeName, defaultValue.ToString()));
420                 }
421                 return;
422             }
423             PrimitiveMapping pm = (PrimitiveMapping)mapping;
424
425             if (comments != null && !pm.TypeDesc.HasDefaultSupport && pm.TypeDesc.IsMappedType) {
426                 // do not generate intializers for the user prefered types if they do not have default capability
427                 DropDefaultAttribute(accessor, comments, pm.TypeDesc.FullName);
428                 return;
429             }
430             if (value == DBNull.Value) {
431                 if (comments != null) {
432                     AddWarningComment(comments, Res.GetString(Res.XmlDropAttributeValue, attributeName, pm.TypeName, defaultValue.ToString()));
433                 }
434                 return;
435             }
436             CodeAttributeArgument[] arguments = null;
437             CodeExpression initExpression = null;
438             
439             if (pm.IsList) {
440                 #if DEBUG
441                     // use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe
442                     if (value.GetType() != typeof(object[])) throw new InvalidOperationException(Res.GetString(Res.XmlInternalErrorDetails, "Default value for list should be object[], not " + value.GetType().Name));
443                 #endif
444
445                 object[] vals = (object[])value;
446                 CodeExpression[] initializers = new CodeExpression[vals.Length];
447                 for (int i = 0; i < vals.Length; i++) {
448                     GetDefaultValueArguments(pm, vals[i], out initializers[i]);
449                 }
450                 initExpression = new CodeArrayCreateExpression(field.Type, initializers);
451                 
452             }
453             else {
454                 arguments = GetDefaultValueArguments(pm, value, out initExpression);
455             }
456
457             if (field != null) {
458                 if (ctor != null) {
459                     AddInitializationStatement(ctor, field, initExpression);
460                 }
461                 else {
462                     field.InitExpression = initExpression;
463                 }
464             }
465             if (arguments != null && pm.TypeDesc.HasDefaultSupport && accessor.IsOptional && !accessor.IsFixed) {
466                 // Add [DefaultValueAttribute]
467                 CodeAttributeDeclaration attribute = new CodeAttributeDeclaration(typeof(DefaultValueAttribute).FullName, arguments);
468                 metadata.Add(attribute);
469             }
470             else if (comments != null) {
471                 DropDefaultAttribute(accessor, comments, memberTypeDesc.FullName);
472             }
473         }
474
475         static void AddInitializationStatement(CodeConstructor ctor, CodeMemberField field, CodeExpression init) {
476             CodeAssignStatement assign = new CodeAssignStatement();
477             assign.Left = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), field.Name);
478             assign.Right = init;
479             ctor.Statements.Add(assign);
480         }
481
482         static void DropDefaultAttribute(Accessor accessor, CodeCommentStatementCollection comments, string type) {
483             if (!accessor.IsFixed && accessor.IsOptional) {
484                 AddWarningComment(comments, Res.GetString(Res.XmlDropDefaultAttribute, type));
485             }
486         }
487
488         CodeTypeDeclaration ExportStruct(StructMapping mapping) {
489             if (mapping.TypeDesc.IsRoot) {
490                 ExportRoot(mapping, typeof(XmlIncludeAttribute));
491                 return null;
492             }
493
494             string className = mapping.TypeDesc.Name;
495             string baseName = mapping.TypeDesc.BaseTypeDesc == null || mapping.TypeDesc.BaseTypeDesc.IsRoot ? string.Empty : mapping.TypeDesc.BaseTypeDesc.FullName;
496
497             CodeTypeDeclaration codeClass = new CodeTypeDeclaration(className);
498             codeClass.IsPartial = CodeProvider.Supports(GeneratorSupport.PartialTypes);
499             codeClass.Comments.Add(new CodeCommentStatement(Res.GetString(Res.XmlRemarks), true));
500
501             CodeNamespace.Types.Add(codeClass);
502
503             CodeConstructor ctor = new CodeConstructor();
504             ctor.Attributes = (ctor.Attributes & ~MemberAttributes.AccessMask) | MemberAttributes.Public;
505             codeClass.Members.Add(ctor);
506             if (mapping.TypeDesc.IsAbstract) {
507                 ctor.Attributes |= MemberAttributes.Abstract;
508             }
509
510             if (baseName != null && baseName.Length > 0) {
511                 codeClass.BaseTypes.Add(baseName);
512             }
513             else
514                 AddPropertyChangedNotifier(codeClass);
515
516             codeClass.TypeAttributes |= TypeAttributes.Public;
517             if (mapping.TypeDesc.IsAbstract) {
518                 codeClass.TypeAttributes |= TypeAttributes.Abstract;
519             }
520
521             AddIncludeMetadata(codeClass.CustomAttributes, mapping, typeof(XmlIncludeAttribute));
522
523             if (mapping.IsSequence) {
524                 int generatedSequence = 0;
525                 for (int i = 0; i < mapping.Members.Length; i++) {
526                     MemberMapping member = mapping.Members[i];
527                     if (member.IsParticle && member.SequenceId < 0)
528                         member.SequenceId = generatedSequence++;
529                 }
530             }
531
532             if (GenerateProperties) {
533                 for (int i = 0; i < mapping.Members.Length; i++) {
534                     ExportProperty(codeClass, mapping.Members[i], mapping.Namespace, mapping.Scope, ctor);
535                 }
536             }
537             else {
538                 for (int i = 0; i < mapping.Members.Length; i++) {
539                     ExportMember(codeClass, mapping.Members[i], mapping.Namespace, ctor);
540                 }
541             }
542
543             for (int i = 0; i < mapping.Members.Length; i++) {
544                 if (mapping.Members[i].Xmlns != null)
545                     continue;
546                 EnsureTypesExported(mapping.Members[i].Elements, mapping.Namespace);
547                 EnsureTypesExported(mapping.Members[i].Attribute, mapping.Namespace);
548                 EnsureTypesExported(mapping.Members[i].Text, mapping.Namespace);
549             }
550
551             if (mapping.BaseMapping != null)
552                 ExportType(mapping.BaseMapping, null, mapping.Namespace, null, false);
553
554             ExportDerivedStructs(mapping);
555             CodeGenerator.ValidateIdentifiers(codeClass);
556             if (ctor.Statements.Count == 0) codeClass.Members.Remove(ctor);
557             return codeClass;
558         }
559
560         [PermissionSet(SecurityAction.InheritanceDemand, Name="FullTrust")]
561         internal override void ExportDerivedStructs(StructMapping mapping) {
562             for (StructMapping derived = mapping.DerivedMappings; derived != null; derived = derived.NextDerivedMapping)
563                 ExportType(derived, mapping.Namespace);
564         }
565
566         /// <include file='doc\XmlCodeExporter.uex' path='docs/doc[@for="XmlCodeExporter.AddMappingMetadata"]/*' />
567         /// <devdoc>
568         ///    <para>[To be supplied.]</para>
569         /// </devdoc>
570         public void AddMappingMetadata(CodeAttributeDeclarationCollection metadata, XmlTypeMapping mapping, string ns) {
571             mapping.CheckShallow();
572             CheckScope(mapping.Scope);
573             // For struct or enum mappings, we generate the XmlRoot on the struct/class/enum.  For primitives 
574             // or arrays, there is nowhere to generate the XmlRoot, so we generate it elsewhere (on the 
575             // method for web services get/post). 
576             if (mapping.Mapping is StructMapping || mapping.Mapping is EnumMapping) return;
577             AddRootMetadata(metadata, mapping.Mapping, Accessor.UnescapeName(mapping.Accessor.Name), mapping.Accessor.Namespace, mapping.Accessor);
578         }
579
580         /// <include file='doc\XmlCodeExporter.uex' path='docs/doc[@for="XmlCodeExporter.AddMappingMetadata1"]/*' />
581         /// <devdoc>
582         ///    <para>[To be supplied.]</para>
583         /// </devdoc>
584         public void AddMappingMetadata(CodeAttributeDeclarationCollection metadata, XmlMemberMapping member, string ns, bool forceUseMemberName) {
585             AddMemberMetadata(null, metadata, member.Mapping, ns, forceUseMemberName, null, null);
586         }         
587          
588         /// <include file='doc\XmlCodeExporter.uex' path='docs/doc[@for="XmlCodeExporter.AddMappingMetadata2"]/*' />
589         /// <devdoc>
590         ///    <para>[To be supplied.]</para>
591         /// </devdoc>
592         public void AddMappingMetadata(CodeAttributeDeclarationCollection metadata, XmlMemberMapping member, string ns) {
593             AddMemberMetadata(null, metadata, member.Mapping, ns, false, null, null);
594         }
595
596         void ExportArrayElements(CodeAttributeDeclarationCollection metadata, ArrayMapping array, string ns, TypeDesc elementTypeDesc, int nestingLevel) {
597             for (int i = 0; i < array.Elements.Length; i++) {
598                 ElementAccessor arrayElement = array.Elements[i];
599                 TypeMapping elementMapping = arrayElement.Mapping;
600                 string elementName = Accessor.UnescapeName(arrayElement.Name);
601                 bool sameName = arrayElement.Mapping.TypeDesc.IsArray ? false : elementName == arrayElement.Mapping.TypeName;
602                 bool sameElementType = elementMapping.TypeDesc == elementTypeDesc;
603                 bool sameElementNs = arrayElement.Form == XmlSchemaForm.Unqualified || arrayElement.Namespace == ns;
604                 bool sameNullable = arrayElement.IsNullable == elementMapping.TypeDesc.IsNullable;
605                 bool defaultForm = arrayElement.Form != XmlSchemaForm.Unqualified;
606                 if (!sameName || !sameElementType || !sameElementNs || !sameNullable || !defaultForm || nestingLevel > 0)
607                     ExportArrayItem(metadata, sameName ? null : elementName, sameElementNs ? null : arrayElement.Namespace, sameElementType ? null : elementMapping.TypeDesc, elementMapping.TypeDesc, arrayElement.IsNullable, defaultForm ? XmlSchemaForm.None : arrayElement.Form, nestingLevel);
608                 if (elementMapping is ArrayMapping)
609                     ExportArrayElements(metadata, (ArrayMapping) elementMapping, ns, elementTypeDesc.ArrayElementTypeDesc, nestingLevel+1);
610             }
611         }
612
613         void AddMemberMetadata(CodeMemberField field, CodeAttributeDeclarationCollection metadata, MemberMapping member, string ns, bool forceUseMemberName, CodeCommentStatementCollection comments, CodeConstructor ctor) {
614             if (member.Xmlns != null) {
615                 CodeAttributeDeclaration attribute = new CodeAttributeDeclaration(typeof(XmlNamespaceDeclarationsAttribute).FullName);
616                 metadata.Add(attribute);
617             }
618             else if (member.Attribute != null) {
619                 AttributeAccessor attribute = member.Attribute;
620                 if (attribute.Any)
621                     ExportAnyAttribute(metadata);
622                 else {
623                     TypeMapping mapping = (TypeMapping)attribute.Mapping;
624                     string attrName = Accessor.UnescapeName(attribute.Name);
625                     bool sameType = mapping.TypeDesc == member.TypeDesc ||
626                         (member.TypeDesc.IsArrayLike && mapping.TypeDesc == member.TypeDesc.ArrayElementTypeDesc);
627                     bool sameName = attrName == member.Name && !forceUseMemberName;
628                     bool sameNs = attribute.Namespace == ns;
629                     bool defaultForm = attribute.Form != XmlSchemaForm.Qualified;
630                     ExportAttribute(metadata, 
631                         sameName ? null : attrName, 
632                         sameNs || defaultForm ? null : attribute.Namespace, 
633                         sameType ? null : mapping.TypeDesc,
634                         mapping.TypeDesc, 
635                         defaultForm ? XmlSchemaForm.None : attribute.Form);
636                 
637                     AddDefaultValueAttribute(field, metadata, attribute.Default, mapping, comments, member.TypeDesc, attribute, ctor);
638                 }
639             }
640             else {
641                 if (member.Text != null) {
642                     TypeMapping mapping = (TypeMapping)member.Text.Mapping;
643                     bool sameType = mapping.TypeDesc == member.TypeDesc ||
644                         (member.TypeDesc.IsArrayLike && mapping.TypeDesc == member.TypeDesc.ArrayElementTypeDesc);
645                     ExportText(metadata, sameType ? null : mapping.TypeDesc, mapping.TypeDesc.IsAmbiguousDataType ? mapping.TypeDesc.DataType.Name : null);
646                 }
647                 if (member.Elements.Length == 1) {
648                     ElementAccessor element = member.Elements[0];
649                     TypeMapping mapping = (TypeMapping)element.Mapping;
650                     string elemName = Accessor.UnescapeName(element.Name);
651                     bool sameName = ((elemName == member.Name) && !forceUseMemberName);                    
652                     bool isArray = mapping is ArrayMapping;
653                     bool sameNs = element.Namespace == ns;
654                     bool defaultForm = element.Form != XmlSchemaForm.Unqualified;
655
656                     if (element.Any)
657                         ExportAnyElement(metadata, elemName, element.Namespace, member.SequenceId);
658                     else if (isArray) {
659                         bool sameType = mapping.TypeDesc == member.TypeDesc;
660                         ArrayMapping array = (ArrayMapping)mapping;
661                         if (!sameName || !sameNs || element.IsNullable || !defaultForm || member.SequenceId != -1)
662                             ExportArray(metadata, sameName ? null : elemName, sameNs ? null : element.Namespace, element.IsNullable, defaultForm ? XmlSchemaForm.None : element.Form, member.SequenceId);
663                         else if (mapping.TypeDesc.ArrayElementTypeDesc == new TypeScope().GetTypeDesc(typeof(byte))) {
664                             // special case for byte[]. It can be a primitive (base64Binary or hexBinary), or it can
665                             // be an array of bytes. Our default is primitive; specify [XmlArray] to get array behavior.
666                             ExportArray(metadata, null, null, false, XmlSchemaForm.None, member.SequenceId);
667                         }
668                         ExportArrayElements(metadata, array, element.Namespace, member.TypeDesc.ArrayElementTypeDesc, 0);
669                     }
670                     else {
671                         bool sameType = mapping.TypeDesc == member.TypeDesc ||
672                             (member.TypeDesc.IsArrayLike && mapping.TypeDesc == member.TypeDesc.ArrayElementTypeDesc);
673                         if (member.TypeDesc.IsArrayLike)
674                             sameName = false;
675                         ExportElement(metadata, sameName ? null : elemName, sameNs ? null : element.Namespace, sameType ? null : mapping.TypeDesc, mapping.TypeDesc, element.IsNullable, defaultForm ? XmlSchemaForm.None : element.Form, member.SequenceId);
676                     }
677                     AddDefaultValueAttribute(field, metadata, element.Default, mapping, comments, member.TypeDesc, element, ctor);
678                 }
679                 else {
680                     for (int i = 0; i < member.Elements.Length; i++) {
681                         ElementAccessor element = member.Elements[i];
682                         string elemName = Accessor.UnescapeName(element.Name);
683                         bool sameNs = element.Namespace == ns;
684                         if (element.Any)
685                             ExportAnyElement(metadata, elemName, element.Namespace, member.SequenceId);
686                         else {
687                             bool defaultForm = element.Form != XmlSchemaForm.Unqualified;
688                             ExportElement(metadata, elemName, sameNs ? null : element.Namespace, ((TypeMapping)element.Mapping).TypeDesc, ((TypeMapping)element.Mapping).TypeDesc, element.IsNullable, defaultForm ? XmlSchemaForm.None : element.Form, member.SequenceId);
689                         }
690                     }
691                 }
692                 if (member.ChoiceIdentifier != null) {
693                     CodeAttributeDeclaration attribute = new CodeAttributeDeclaration(typeof(XmlChoiceIdentifierAttribute).FullName);
694                     attribute.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression(member.ChoiceIdentifier.MemberName)));
695                     metadata.Add(attribute);
696                 }
697                 if (member.Ignore) {
698                     CodeAttributeDeclaration attribute = new CodeAttributeDeclaration(typeof(XmlIgnoreAttribute).FullName);
699                     metadata.Add(attribute);
700                 }
701             }
702         }
703
704         void ExportMember(CodeTypeDeclaration codeClass, MemberMapping member, string ns, CodeConstructor ctor) {
705             string fieldType = member.GetTypeName(CodeProvider);
706             CodeMemberField field = new CodeMemberField(fieldType, member.Name);
707             field.Attributes = (field.Attributes & ~MemberAttributes.AccessMask) | MemberAttributes.Public;
708             field.Comments.Add(new CodeCommentStatement(Res.GetString(Res.XmlRemarks), true));
709             codeClass.Members.Add(field);
710             AddMemberMetadata(field, field.CustomAttributes, member, ns, false, field.Comments, ctor);
711
712             if (member.CheckSpecified != SpecifiedAccessor.None) {
713                 field = new CodeMemberField(typeof(bool).FullName, member.Name + "Specified");
714                 field.Attributes = (field.Attributes & ~MemberAttributes.AccessMask) | MemberAttributes.Public;
715                 field.Comments.Add(new CodeCommentStatement(Res.GetString(Res.XmlRemarks), true));
716                 CodeAttributeDeclaration attribute = new CodeAttributeDeclaration(typeof(XmlIgnoreAttribute).FullName);
717                 field.CustomAttributes.Add(attribute);
718                 codeClass.Members.Add(field);
719             }
720         }
721
722         void ExportProperty(CodeTypeDeclaration codeClass, MemberMapping member, string ns, CodeIdentifiers memberScope, CodeConstructor ctor) {
723             string fieldName = memberScope.AddUnique(MakeFieldName(member.Name), member);
724             string fieldType = member.GetTypeName(CodeProvider);
725             // need to create a private field
726             CodeMemberField field = new CodeMemberField(fieldType, fieldName);
727             field.Attributes = MemberAttributes.Private;
728             codeClass.Members.Add(field);
729
730             CodeMemberProperty prop = CreatePropertyDeclaration(field, member.Name, fieldType);
731             prop.Comments.Add(new CodeCommentStatement(Res.GetString(Res.XmlRemarks), true));
732             AddMemberMetadata(field, prop.CustomAttributes, member, ns, false, prop.Comments, ctor);
733             codeClass.Members.Add(prop);
734
735             if (member.CheckSpecified != SpecifiedAccessor.None) {
736                 field = new CodeMemberField(typeof(bool).FullName, fieldName + "Specified");
737                 field.Attributes = MemberAttributes.Private;
738                 codeClass.Members.Add(field);
739                 
740                 prop = CreatePropertyDeclaration(field, member.Name + "Specified", typeof(bool).FullName);
741                 prop.Comments.Add(new CodeCommentStatement(Res.GetString(Res.XmlRemarks), true));
742                 CodeAttributeDeclaration attribute = new CodeAttributeDeclaration(typeof(XmlIgnoreAttribute).FullName);
743                 prop.CustomAttributes.Add(attribute);
744                 codeClass.Members.Add(prop);
745             }
746         }
747
748         void ExportText(CodeAttributeDeclarationCollection metadata, TypeDesc typeDesc, string dataType) {
749             CodeAttributeDeclaration attribute = new CodeAttributeDeclaration(typeof(XmlTextAttribute).FullName);
750             if (typeDesc != null) {
751                 attribute.Arguments.Add(new CodeAttributeArgument(new CodeTypeOfExpression(typeDesc.FullName)));
752             }
753             if (dataType != null) {
754                 attribute.Arguments.Add(new CodeAttributeArgument("DataType", new CodePrimitiveExpression(dataType)));
755             }
756             metadata.Add(attribute);
757         }
758         
759         void ExportAttribute(CodeAttributeDeclarationCollection metadata, string name, string ns, TypeDesc typeDesc, TypeDesc dataTypeDesc, XmlSchemaForm form) {
760             ExportMetadata(metadata, typeof(XmlAttributeAttribute), name, ns, typeDesc, dataTypeDesc, null, form, 0, -1);
761         }
762         
763         void ExportArrayItem(CodeAttributeDeclarationCollection metadata, string name, string ns, TypeDesc typeDesc, TypeDesc dataTypeDesc, bool isNullable, XmlSchemaForm form, int nestingLevel) {
764             ExportMetadata(metadata, typeof(XmlArrayItemAttribute), name, ns, typeDesc, dataTypeDesc, isNullable ? null : (object)false, form, nestingLevel, -1);
765         }
766         
767         void ExportElement(CodeAttributeDeclarationCollection metadata, string name, string ns, TypeDesc typeDesc, TypeDesc dataTypeDesc, bool isNullable, XmlSchemaForm form, int sequenceId) {
768             ExportMetadata(metadata, typeof(XmlElementAttribute), name, ns, typeDesc, dataTypeDesc, isNullable ? (object)true : null, form, 0, sequenceId);
769         }
770
771         void ExportArray(CodeAttributeDeclarationCollection metadata, string name, string ns, bool isNullable, XmlSchemaForm form, int sequenceId) {
772             ExportMetadata(metadata, typeof(XmlArrayAttribute), name, ns, null, null, isNullable ? (object)true : null, form, 0, sequenceId);
773         }
774         
775         void ExportMetadata(CodeAttributeDeclarationCollection metadata, Type attributeType, string name, string ns, TypeDesc typeDesc, TypeDesc dataTypeDesc, object isNullable, XmlSchemaForm form, int nestingLevel, int sequenceId) {
776             CodeAttributeDeclaration attribute = new CodeAttributeDeclaration(attributeType.FullName);
777             if (name != null) {
778                 attribute.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression(name)));
779             }
780             if (typeDesc != null) {
781                 if (isNullable != null && (bool)isNullable && typeDesc.IsValueType && !typeDesc.IsMappedType && CodeProvider.Supports(GeneratorSupport.GenericTypeReference)) {
782                     attribute.Arguments.Add(new CodeAttributeArgument(new CodeTypeOfExpression("System.Nullable`1[" + typeDesc.FullName + "]")));
783                     isNullable = null;
784                 }
785                 else {
786                     attribute.Arguments.Add(new CodeAttributeArgument(new CodeTypeOfExpression(typeDesc.FullName)));
787                 }
788             }
789             if (form != XmlSchemaForm.None) {
790                 attribute.Arguments.Add(new CodeAttributeArgument("Form", new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(XmlSchemaForm).FullName), Enum.Format(typeof(XmlSchemaForm), form, "G"))));
791
792                 if (form == XmlSchemaForm.Unqualified && ns != null && ns.Length == 0) {
793                     ns = null;
794                 }
795             }
796             if (ns != null ) {
797                 attribute.Arguments.Add(new CodeAttributeArgument("Namespace", new CodePrimitiveExpression(ns)));
798             }
799             if (dataTypeDesc != null && dataTypeDesc.IsAmbiguousDataType && !dataTypeDesc.IsMappedType) {
800                 attribute.Arguments.Add(new CodeAttributeArgument("DataType", new CodePrimitiveExpression(dataTypeDesc.DataType.Name)));
801             }
802             if (isNullable != null) {
803                 attribute.Arguments.Add(new CodeAttributeArgument("IsNullable", new CodePrimitiveExpression((bool)isNullable)));
804             }
805             if (nestingLevel > 0) {
806                 attribute.Arguments.Add(new CodeAttributeArgument("NestingLevel", new CodePrimitiveExpression(nestingLevel)));
807             }
808             if (sequenceId >= 0) {
809                 attribute.Arguments.Add(new CodeAttributeArgument("Order", new CodePrimitiveExpression(sequenceId)));
810             }
811             if (attribute.Arguments.Count == 0 && attributeType == typeof(XmlElementAttribute)) return;
812             metadata.Add(attribute);
813         }
814
815         void ExportAnyElement(CodeAttributeDeclarationCollection metadata, string name, string ns, int sequenceId) {
816             CodeAttributeDeclaration attribute = new CodeAttributeDeclaration(typeof(XmlAnyElementAttribute).FullName);
817             if (name != null && name.Length > 0) {
818                 attribute.Arguments.Add(new CodeAttributeArgument("Name", new CodePrimitiveExpression(name)));
819             }
820             if (ns != null) {
821                 attribute.Arguments.Add(new CodeAttributeArgument("Namespace", new CodePrimitiveExpression(ns)));
822             }
823             if (sequenceId >= 0) {
824                 attribute.Arguments.Add(new CodeAttributeArgument("Order", new CodePrimitiveExpression(sequenceId)));
825             }
826             metadata.Add(attribute);
827         }
828
829         void ExportAnyAttribute(CodeAttributeDeclarationCollection metadata) {
830             metadata.Add(new CodeAttributeDeclaration(typeof(XmlAnyAttributeAttribute).FullName));
831         }
832
833         internal override void EnsureTypesExported(Accessor[] accessors, string ns) {
834             if (accessors == null) return;
835             for (int i = 0; i < accessors.Length; i++)
836                 EnsureTypesExported(accessors[i], ns);
837         }
838
839         void EnsureTypesExported(Accessor accessor, string ns) {
840             if (accessor == null) return;
841             ExportType(accessor.Mapping, null, ns, null, false);
842         }
843     }
844 }