Initial commit
[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">[....]</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                 DropDefaultAttribute(accessor, comments, memberTypeDesc.FullName);
418                 AddWarningComment(comments, Res.GetString(Res.XmlDropNonPrimitiveAttributeValue, attributeName, defaultValue.ToString()));
419                 return;
420             }
421             PrimitiveMapping pm = (PrimitiveMapping)mapping;
422
423             if (comments != null && !pm.TypeDesc.HasDefaultSupport && pm.TypeDesc.IsMappedType) {
424                 // do not generate intializers for the user prefered types if they do not have default capability
425                 DropDefaultAttribute(accessor, comments, pm.TypeDesc.FullName);
426                 return;
427             }
428             if (value == DBNull.Value) {
429                 if (comments != null) {
430                     AddWarningComment(comments, Res.GetString(Res.XmlDropAttributeValue, attributeName, pm.TypeName, defaultValue.ToString()));
431                 }
432                 return;
433             }
434             CodeAttributeArgument[] arguments = null;
435             CodeExpression initExpression = null;
436             
437             if (pm.IsList) {
438                 #if DEBUG
439                     // use exception in the place of Debug.Assert to avoid throwing asserts from a server process such as aspnet_ewp.exe
440                     if (value.GetType() != typeof(object[])) throw new InvalidOperationException(Res.GetString(Res.XmlInternalErrorDetails, "Default value for list should be object[], not " + value.GetType().Name));
441                 #endif
442
443                 object[] vals = (object[])value;
444                 CodeExpression[] initializers = new CodeExpression[vals.Length];
445                 for (int i = 0; i < vals.Length; i++) {
446                     GetDefaultValueArguments(pm, vals[i], out initializers[i]);
447                 }
448                 initExpression = new CodeArrayCreateExpression(field.Type, initializers);
449                 
450             }
451             else {
452                 arguments = GetDefaultValueArguments(pm, value, out initExpression);
453             }
454
455             if (field != null) {
456                 if (ctor != null) {
457                     AddInitializationStatement(ctor, field, initExpression);
458                 }
459                 else {
460                     field.InitExpression = initExpression;
461                 }
462             }
463             if (arguments != null && pm.TypeDesc.HasDefaultSupport && accessor.IsOptional && !accessor.IsFixed) {
464                 // Add [DefaultValueAttribute]
465                 CodeAttributeDeclaration attribute = new CodeAttributeDeclaration(typeof(DefaultValueAttribute).FullName, arguments);
466                 metadata.Add(attribute);
467             }
468             else if (comments != null) {
469                 DropDefaultAttribute(accessor, comments, memberTypeDesc.FullName);
470             }
471         }
472
473         static void AddInitializationStatement(CodeConstructor ctor, CodeMemberField field, CodeExpression init) {
474             CodeAssignStatement assign = new CodeAssignStatement();
475             assign.Left = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), field.Name);
476             assign.Right = init;
477             ctor.Statements.Add(assign);
478         }
479
480         static void DropDefaultAttribute(Accessor accessor, CodeCommentStatementCollection comments, string type) {
481             if (!accessor.IsFixed && accessor.IsOptional) {
482                 AddWarningComment(comments, Res.GetString(Res.XmlDropDefaultAttribute, type));
483             }
484         }
485
486         CodeTypeDeclaration ExportStruct(StructMapping mapping) {
487             if (mapping.TypeDesc.IsRoot) {
488                 ExportRoot(mapping, typeof(XmlIncludeAttribute));
489                 return null;
490             }
491
492             string className = mapping.TypeDesc.Name;
493             string baseName = mapping.TypeDesc.BaseTypeDesc == null || mapping.TypeDesc.BaseTypeDesc.IsRoot ? string.Empty : mapping.TypeDesc.BaseTypeDesc.FullName;
494
495             CodeTypeDeclaration codeClass = new CodeTypeDeclaration(className);
496             codeClass.IsPartial = CodeProvider.Supports(GeneratorSupport.PartialTypes);
497             codeClass.Comments.Add(new CodeCommentStatement(Res.GetString(Res.XmlRemarks), true));
498
499             CodeNamespace.Types.Add(codeClass);
500
501             CodeConstructor ctor = new CodeConstructor();
502             ctor.Attributes = (ctor.Attributes & ~MemberAttributes.AccessMask) | MemberAttributes.Public;
503             codeClass.Members.Add(ctor);
504             if (mapping.TypeDesc.IsAbstract) {
505                 ctor.Attributes |= MemberAttributes.Abstract;
506             }
507
508             if (baseName != null && baseName.Length > 0) {
509                 codeClass.BaseTypes.Add(baseName);
510             }
511             else
512                 AddPropertyChangedNotifier(codeClass);
513
514             codeClass.TypeAttributes |= TypeAttributes.Public;
515             if (mapping.TypeDesc.IsAbstract) {
516                 codeClass.TypeAttributes |= TypeAttributes.Abstract;
517             }
518
519             AddIncludeMetadata(codeClass.CustomAttributes, mapping, typeof(XmlIncludeAttribute));
520
521             if (mapping.IsSequence) {
522                 int generatedSequence = 0;
523                 for (int i = 0; i < mapping.Members.Length; i++) {
524                     MemberMapping member = mapping.Members[i];
525                     if (member.IsParticle && member.SequenceId < 0)
526                         member.SequenceId = generatedSequence++;
527                 }
528             }
529
530             if (GenerateProperties) {
531                 for (int i = 0; i < mapping.Members.Length; i++) {
532                     ExportProperty(codeClass, mapping.Members[i], mapping.Namespace, mapping.Scope, ctor);
533                 }
534             }
535             else {
536                 for (int i = 0; i < mapping.Members.Length; i++) {
537                     ExportMember(codeClass, mapping.Members[i], mapping.Namespace, ctor);
538                 }
539             }
540
541             for (int i = 0; i < mapping.Members.Length; i++) {
542                 if (mapping.Members[i].Xmlns != null)
543                     continue;
544                 EnsureTypesExported(mapping.Members[i].Elements, mapping.Namespace);
545                 EnsureTypesExported(mapping.Members[i].Attribute, mapping.Namespace);
546                 EnsureTypesExported(mapping.Members[i].Text, mapping.Namespace);
547             }
548
549             if (mapping.BaseMapping != null)
550                 ExportType(mapping.BaseMapping, null, mapping.Namespace, null, false);
551
552             ExportDerivedStructs(mapping);
553             CodeGenerator.ValidateIdentifiers(codeClass);
554             if (ctor.Statements.Count == 0) codeClass.Members.Remove(ctor);
555             return codeClass;
556         }
557
558         [PermissionSet(SecurityAction.InheritanceDemand, Name="FullTrust")]
559         internal override void ExportDerivedStructs(StructMapping mapping) {
560             for (StructMapping derived = mapping.DerivedMappings; derived != null; derived = derived.NextDerivedMapping)
561                 ExportType(derived, mapping.Namespace);
562         }
563
564         /// <include file='doc\XmlCodeExporter.uex' path='docs/doc[@for="XmlCodeExporter.AddMappingMetadata"]/*' />
565         /// <devdoc>
566         ///    <para>[To be supplied.]</para>
567         /// </devdoc>
568         public void AddMappingMetadata(CodeAttributeDeclarationCollection metadata, XmlTypeMapping mapping, string ns) {
569             mapping.CheckShallow();
570             CheckScope(mapping.Scope);
571             // For struct or enum mappings, we generate the XmlRoot on the struct/class/enum.  For primitives 
572             // or arrays, there is nowhere to generate the XmlRoot, so we generate it elsewhere (on the 
573             // method for web services get/post). 
574             if (mapping.Mapping is StructMapping || mapping.Mapping is EnumMapping) return;
575             AddRootMetadata(metadata, mapping.Mapping, Accessor.UnescapeName(mapping.Accessor.Name), mapping.Accessor.Namespace, mapping.Accessor);
576         }
577
578         /// <include file='doc\XmlCodeExporter.uex' path='docs/doc[@for="XmlCodeExporter.AddMappingMetadata1"]/*' />
579         /// <devdoc>
580         ///    <para>[To be supplied.]</para>
581         /// </devdoc>
582         public void AddMappingMetadata(CodeAttributeDeclarationCollection metadata, XmlMemberMapping member, string ns, bool forceUseMemberName) {
583             AddMemberMetadata(null, metadata, member.Mapping, ns, forceUseMemberName, null, null);
584         }         
585          
586         /// <include file='doc\XmlCodeExporter.uex' path='docs/doc[@for="XmlCodeExporter.AddMappingMetadata2"]/*' />
587         /// <devdoc>
588         ///    <para>[To be supplied.]</para>
589         /// </devdoc>
590         public void AddMappingMetadata(CodeAttributeDeclarationCollection metadata, XmlMemberMapping member, string ns) {
591             AddMemberMetadata(null, metadata, member.Mapping, ns, false, null, null);
592         }
593
594         void ExportArrayElements(CodeAttributeDeclarationCollection metadata, ArrayMapping array, string ns, TypeDesc elementTypeDesc, int nestingLevel) {
595             for (int i = 0; i < array.Elements.Length; i++) {
596                 ElementAccessor arrayElement = array.Elements[i];
597                 TypeMapping elementMapping = arrayElement.Mapping;
598                 string elementName = Accessor.UnescapeName(arrayElement.Name);
599                 bool sameName = arrayElement.Mapping.TypeDesc.IsArray ? false : elementName == arrayElement.Mapping.TypeName;
600                 bool sameElementType = elementMapping.TypeDesc == elementTypeDesc;
601                 bool sameElementNs = arrayElement.Form == XmlSchemaForm.Unqualified || arrayElement.Namespace == ns;
602                 bool sameNullable = arrayElement.IsNullable == elementMapping.TypeDesc.IsNullable;
603                 bool defaultForm = arrayElement.Form != XmlSchemaForm.Unqualified;
604                 if (!sameName || !sameElementType || !sameElementNs || !sameNullable || !defaultForm || nestingLevel > 0)
605                     ExportArrayItem(metadata, sameName ? null : elementName, sameElementNs ? null : arrayElement.Namespace, sameElementType ? null : elementMapping.TypeDesc, elementMapping.TypeDesc, arrayElement.IsNullable, defaultForm ? XmlSchemaForm.None : arrayElement.Form, nestingLevel);
606                 if (elementMapping is ArrayMapping)
607                     ExportArrayElements(metadata, (ArrayMapping) elementMapping, ns, elementTypeDesc.ArrayElementTypeDesc, nestingLevel+1);
608             }
609         }
610
611         void AddMemberMetadata(CodeMemberField field, CodeAttributeDeclarationCollection metadata, MemberMapping member, string ns, bool forceUseMemberName, CodeCommentStatementCollection comments, CodeConstructor ctor) {
612             if (member.Xmlns != null) {
613                 CodeAttributeDeclaration attribute = new CodeAttributeDeclaration(typeof(XmlNamespaceDeclarationsAttribute).FullName);
614                 metadata.Add(attribute);
615             }
616             else if (member.Attribute != null) {
617                 AttributeAccessor attribute = member.Attribute;
618                 if (attribute.Any)
619                     ExportAnyAttribute(metadata);
620                 else {
621                     TypeMapping mapping = (TypeMapping)attribute.Mapping;
622                     string attrName = Accessor.UnescapeName(attribute.Name);
623                     bool sameType = mapping.TypeDesc == member.TypeDesc ||
624                         (member.TypeDesc.IsArrayLike && mapping.TypeDesc == member.TypeDesc.ArrayElementTypeDesc);
625                     bool sameName = attrName == member.Name && !forceUseMemberName;
626                     bool sameNs = attribute.Namespace == ns;
627                     bool defaultForm = attribute.Form != XmlSchemaForm.Qualified;
628                     ExportAttribute(metadata, 
629                         sameName ? null : attrName, 
630                         sameNs || defaultForm ? null : attribute.Namespace, 
631                         sameType ? null : mapping.TypeDesc,
632                         mapping.TypeDesc, 
633                         defaultForm ? XmlSchemaForm.None : attribute.Form);
634                 
635                     AddDefaultValueAttribute(field, metadata, attribute.Default, mapping, comments, member.TypeDesc, attribute, ctor);
636                 }
637             }
638             else {
639                 if (member.Text != null) {
640                     TypeMapping mapping = (TypeMapping)member.Text.Mapping;
641                     bool sameType = mapping.TypeDesc == member.TypeDesc ||
642                         (member.TypeDesc.IsArrayLike && mapping.TypeDesc == member.TypeDesc.ArrayElementTypeDesc);
643                     ExportText(metadata, sameType ? null : mapping.TypeDesc, mapping.TypeDesc.IsAmbiguousDataType ? mapping.TypeDesc.DataType.Name : null);
644                 }
645                 if (member.Elements.Length == 1) {
646                     ElementAccessor element = member.Elements[0];
647                     TypeMapping mapping = (TypeMapping)element.Mapping;
648                     string elemName = Accessor.UnescapeName(element.Name);
649                     bool sameName = ((elemName == member.Name) && !forceUseMemberName);                    
650                     bool isArray = mapping is ArrayMapping;
651                     bool sameNs = element.Namespace == ns;
652                     bool defaultForm = element.Form != XmlSchemaForm.Unqualified;
653
654                     if (element.Any)
655                         ExportAnyElement(metadata, elemName, element.Namespace, member.SequenceId);
656                     else if (isArray) {
657                         bool sameType = mapping.TypeDesc == member.TypeDesc;
658                         ArrayMapping array = (ArrayMapping)mapping;
659                         if (!sameName || !sameNs || element.IsNullable || !defaultForm || member.SequenceId != -1)
660                             ExportArray(metadata, sameName ? null : elemName, sameNs ? null : element.Namespace, element.IsNullable, defaultForm ? XmlSchemaForm.None : element.Form, member.SequenceId);
661                         else if (mapping.TypeDesc.ArrayElementTypeDesc == new TypeScope().GetTypeDesc(typeof(byte))) {
662                             // special case for byte[]. It can be a primitive (base64Binary or hexBinary), or it can
663                             // be an array of bytes. Our default is primitive; specify [XmlArray] to get array behavior.
664                             ExportArray(metadata, null, null, false, XmlSchemaForm.None, member.SequenceId);
665                         }
666                         ExportArrayElements(metadata, array, element.Namespace, member.TypeDesc.ArrayElementTypeDesc, 0);
667                     }
668                     else {
669                         bool sameType = mapping.TypeDesc == member.TypeDesc ||
670                             (member.TypeDesc.IsArrayLike && mapping.TypeDesc == member.TypeDesc.ArrayElementTypeDesc);
671                         if (member.TypeDesc.IsArrayLike)
672                             sameName = false;
673                         ExportElement(metadata, sameName ? null : elemName, sameNs ? null : element.Namespace, sameType ? null : mapping.TypeDesc, mapping.TypeDesc, element.IsNullable, defaultForm ? XmlSchemaForm.None : element.Form, member.SequenceId);
674                     }
675                     AddDefaultValueAttribute(field, metadata, element.Default, mapping, comments, member.TypeDesc, element, ctor);
676                 }
677                 else {
678                     for (int i = 0; i < member.Elements.Length; i++) {
679                         ElementAccessor element = member.Elements[i];
680                         string elemName = Accessor.UnescapeName(element.Name);
681                         bool sameNs = element.Namespace == ns;
682                         if (element.Any)
683                             ExportAnyElement(metadata, elemName, element.Namespace, member.SequenceId);
684                         else {
685                             bool defaultForm = element.Form != XmlSchemaForm.Unqualified;
686                             ExportElement(metadata, elemName, sameNs ? null : element.Namespace, ((TypeMapping)element.Mapping).TypeDesc, ((TypeMapping)element.Mapping).TypeDesc, element.IsNullable, defaultForm ? XmlSchemaForm.None : element.Form, member.SequenceId);
687                         }
688                     }
689                 }
690                 if (member.ChoiceIdentifier != null) {
691                     CodeAttributeDeclaration attribute = new CodeAttributeDeclaration(typeof(XmlChoiceIdentifierAttribute).FullName);
692                     attribute.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression(member.ChoiceIdentifier.MemberName)));
693                     metadata.Add(attribute);
694                 }
695                 if (member.Ignore) {
696                     CodeAttributeDeclaration attribute = new CodeAttributeDeclaration(typeof(XmlIgnoreAttribute).FullName);
697                     metadata.Add(attribute);
698                 }
699             }
700         }
701
702         void ExportMember(CodeTypeDeclaration codeClass, MemberMapping member, string ns, CodeConstructor ctor) {
703             string fieldType = member.GetTypeName(CodeProvider);
704             CodeMemberField field = new CodeMemberField(fieldType, member.Name);
705             field.Attributes = (field.Attributes & ~MemberAttributes.AccessMask) | MemberAttributes.Public;
706             field.Comments.Add(new CodeCommentStatement(Res.GetString(Res.XmlRemarks), true));
707             codeClass.Members.Add(field);
708             AddMemberMetadata(field, field.CustomAttributes, member, ns, false, field.Comments, ctor);
709
710             if (member.CheckSpecified != SpecifiedAccessor.None) {
711                 field = new CodeMemberField(typeof(bool).FullName, member.Name + "Specified");
712                 field.Attributes = (field.Attributes & ~MemberAttributes.AccessMask) | MemberAttributes.Public;
713                 field.Comments.Add(new CodeCommentStatement(Res.GetString(Res.XmlRemarks), true));
714                 CodeAttributeDeclaration attribute = new CodeAttributeDeclaration(typeof(XmlIgnoreAttribute).FullName);
715                 field.CustomAttributes.Add(attribute);
716                 codeClass.Members.Add(field);
717             }
718         }
719
720         void ExportProperty(CodeTypeDeclaration codeClass, MemberMapping member, string ns, CodeIdentifiers memberScope, CodeConstructor ctor) {
721             string fieldName = memberScope.AddUnique(MakeFieldName(member.Name), member);
722             string fieldType = member.GetTypeName(CodeProvider);
723             // need to create a private field
724             CodeMemberField field = new CodeMemberField(fieldType, fieldName);
725             field.Attributes = MemberAttributes.Private;
726             codeClass.Members.Add(field);
727
728             CodeMemberProperty prop = CreatePropertyDeclaration(field, member.Name, fieldType);
729             prop.Comments.Add(new CodeCommentStatement(Res.GetString(Res.XmlRemarks), true));
730             AddMemberMetadata(field, prop.CustomAttributes, member, ns, false, prop.Comments, ctor);
731             codeClass.Members.Add(prop);
732
733             if (member.CheckSpecified != SpecifiedAccessor.None) {
734                 field = new CodeMemberField(typeof(bool).FullName, fieldName + "Specified");
735                 field.Attributes = MemberAttributes.Private;
736                 codeClass.Members.Add(field);
737                 
738                 prop = CreatePropertyDeclaration(field, member.Name + "Specified", typeof(bool).FullName);
739                 prop.Comments.Add(new CodeCommentStatement(Res.GetString(Res.XmlRemarks), true));
740                 CodeAttributeDeclaration attribute = new CodeAttributeDeclaration(typeof(XmlIgnoreAttribute).FullName);
741                 prop.CustomAttributes.Add(attribute);
742                 codeClass.Members.Add(prop);
743             }
744         }
745
746         void ExportText(CodeAttributeDeclarationCollection metadata, TypeDesc typeDesc, string dataType) {
747             CodeAttributeDeclaration attribute = new CodeAttributeDeclaration(typeof(XmlTextAttribute).FullName);
748             if (typeDesc != null) {
749                 attribute.Arguments.Add(new CodeAttributeArgument(new CodeTypeOfExpression(typeDesc.FullName)));
750             }
751             if (dataType != null) {
752                 attribute.Arguments.Add(new CodeAttributeArgument("DataType", new CodePrimitiveExpression(dataType)));
753             }
754             metadata.Add(attribute);
755         }
756         
757         void ExportAttribute(CodeAttributeDeclarationCollection metadata, string name, string ns, TypeDesc typeDesc, TypeDesc dataTypeDesc, XmlSchemaForm form) {
758             ExportMetadata(metadata, typeof(XmlAttributeAttribute), name, ns, typeDesc, dataTypeDesc, null, form, 0, -1);
759         }
760         
761         void ExportArrayItem(CodeAttributeDeclarationCollection metadata, string name, string ns, TypeDesc typeDesc, TypeDesc dataTypeDesc, bool isNullable, XmlSchemaForm form, int nestingLevel) {
762             ExportMetadata(metadata, typeof(XmlArrayItemAttribute), name, ns, typeDesc, dataTypeDesc, isNullable ? null : (object)false, form, nestingLevel, -1);
763         }
764         
765         void ExportElement(CodeAttributeDeclarationCollection metadata, string name, string ns, TypeDesc typeDesc, TypeDesc dataTypeDesc, bool isNullable, XmlSchemaForm form, int sequenceId) {
766             ExportMetadata(metadata, typeof(XmlElementAttribute), name, ns, typeDesc, dataTypeDesc, isNullable ? (object)true : null, form, 0, sequenceId);
767         }
768
769         void ExportArray(CodeAttributeDeclarationCollection metadata, string name, string ns, bool isNullable, XmlSchemaForm form, int sequenceId) {
770             ExportMetadata(metadata, typeof(XmlArrayAttribute), name, ns, null, null, isNullable ? (object)true : null, form, 0, sequenceId);
771         }
772         
773         void ExportMetadata(CodeAttributeDeclarationCollection metadata, Type attributeType, string name, string ns, TypeDesc typeDesc, TypeDesc dataTypeDesc, object isNullable, XmlSchemaForm form, int nestingLevel, int sequenceId) {
774             CodeAttributeDeclaration attribute = new CodeAttributeDeclaration(attributeType.FullName);
775             if (name != null) {
776                 attribute.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression(name)));
777             }
778             if (typeDesc != null) {
779                 if (isNullable != null && (bool)isNullable && typeDesc.IsValueType && !typeDesc.IsMappedType && CodeProvider.Supports(GeneratorSupport.GenericTypeReference)) {
780                     attribute.Arguments.Add(new CodeAttributeArgument(new CodeTypeOfExpression("System.Nullable`1[" + typeDesc.FullName + "]")));
781                     isNullable = null;
782                 }
783                 else {
784                     attribute.Arguments.Add(new CodeAttributeArgument(new CodeTypeOfExpression(typeDesc.FullName)));
785                 }
786             }
787             if (form != XmlSchemaForm.None) {
788                 attribute.Arguments.Add(new CodeAttributeArgument("Form", new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(typeof(XmlSchemaForm).FullName), Enum.Format(typeof(XmlSchemaForm), form, "G"))));
789
790                 if (form == XmlSchemaForm.Unqualified && ns != null && ns.Length == 0) {
791                     ns = null;
792                 }
793             }
794             if (ns != null ) {
795                 attribute.Arguments.Add(new CodeAttributeArgument("Namespace", new CodePrimitiveExpression(ns)));
796             }
797             if (dataTypeDesc != null && dataTypeDesc.IsAmbiguousDataType && !dataTypeDesc.IsMappedType) {
798                 attribute.Arguments.Add(new CodeAttributeArgument("DataType", new CodePrimitiveExpression(dataTypeDesc.DataType.Name)));
799             }
800             if (isNullable != null) {
801                 attribute.Arguments.Add(new CodeAttributeArgument("IsNullable", new CodePrimitiveExpression((bool)isNullable)));
802             }
803             if (nestingLevel > 0) {
804                 attribute.Arguments.Add(new CodeAttributeArgument("NestingLevel", new CodePrimitiveExpression(nestingLevel)));
805             }
806             if (sequenceId >= 0) {
807                 attribute.Arguments.Add(new CodeAttributeArgument("Order", new CodePrimitiveExpression(sequenceId)));
808             }
809             if (attribute.Arguments.Count == 0 && attributeType == typeof(XmlElementAttribute)) return;
810             metadata.Add(attribute);
811         }
812
813         void ExportAnyElement(CodeAttributeDeclarationCollection metadata, string name, string ns, int sequenceId) {
814             CodeAttributeDeclaration attribute = new CodeAttributeDeclaration(typeof(XmlAnyElementAttribute).FullName);
815             if (name != null && name.Length > 0) {
816                 attribute.Arguments.Add(new CodeAttributeArgument("Name", new CodePrimitiveExpression(name)));
817             }
818             if (ns != null) {
819                 attribute.Arguments.Add(new CodeAttributeArgument("Namespace", new CodePrimitiveExpression(ns)));
820             }
821             if (sequenceId >= 0) {
822                 attribute.Arguments.Add(new CodeAttributeArgument("Order", new CodePrimitiveExpression(sequenceId)));
823             }
824             metadata.Add(attribute);
825         }
826
827         void ExportAnyAttribute(CodeAttributeDeclarationCollection metadata) {
828             metadata.Add(new CodeAttributeDeclaration(typeof(XmlAnyAttributeAttribute).FullName));
829         }
830
831         internal override void EnsureTypesExported(Accessor[] accessors, string ns) {
832             if (accessors == null) return;
833             for (int i = 0; i < accessors.Length; i++)
834                 EnsureTypesExported(accessors[i], ns);
835         }
836
837         void EnsureTypesExported(Accessor accessor, string ns) {
838             if (accessor == null) return;
839             ExportType(accessor.Mapping, null, ns, null, false);
840         }
841     }
842 }