1 //------------------------------------------------------------------------------
2 // <copyright file="XmlCodeExporter.cs" company="Microsoft">
3 // Copyright (c) Microsoft Corporation. All rights reserved.
5 // <owner current="true" primary="true">[....]</owner>
6 //------------------------------------------------------------------------------
8 namespace System.Xml.Serialization {
11 using System.Collections;
13 using System.ComponentModel;
14 using System.Xml.Schema;
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;
23 /// <include file='doc\XmlCodeExporter.uex' path='docs/doc[@for="XmlCodeExporter"]/*' />
26 /// <para>[To be supplied.]</para>
28 public class XmlCodeExporter : CodeExporter {
29 /// <include file='doc\XmlCodeExporter.uex' path='docs/doc[@for="XmlCodeExporter.XmlCodeExporter"]/*' />
31 /// <para>[To be supplied.]</para>
33 public XmlCodeExporter(CodeNamespace codeNamespace) : base(codeNamespace, null, null, CodeGenerationOptions.GenerateProperties, null) {}
35 /// <include file='doc\XmlCodeExporter.uex' path='docs/doc[@for="XmlCodeExporter.XmlCodeExporter1"]/*' />
37 /// <para>[To be supplied.]</para>
39 public XmlCodeExporter(CodeNamespace codeNamespace, CodeCompileUnit codeCompileUnit) : base(codeNamespace, codeCompileUnit, null, CodeGenerationOptions.GenerateProperties, null) {}
41 /// <include file='doc\XmlCodeExporter.uex' path='docs/doc[@for="XmlCodeExporter.XmlCodeExporter2"]/*' />
43 /// <para>[To be supplied.]</para>
45 public XmlCodeExporter(CodeNamespace codeNamespace, CodeCompileUnit codeCompileUnit, CodeGenerationOptions options)
46 : base(codeNamespace, codeCompileUnit, null, options, null) {}
48 /// <include file='doc\XmlCodeExporter.uex' path='docs/doc[@for="XmlCodeExporter.XmlCodeExporter3"]/*' />
50 /// <para>[To be supplied.]</para>
52 public XmlCodeExporter(CodeNamespace codeNamespace, CodeCompileUnit codeCompileUnit, CodeGenerationOptions options, Hashtable mappings)
53 : base(codeNamespace, codeCompileUnit, null, options, mappings) {}
55 /// <include file='doc\XmlCodeExporter.uex' path='docs/doc[@for="XmlCodeExporter.XmlCodeExporter4"]/*' />
57 /// <para>[To be supplied.]</para>
59 public XmlCodeExporter(CodeNamespace codeNamespace, CodeCompileUnit codeCompileUnit, CodeDomProvider codeProvider, CodeGenerationOptions options, Hashtable mappings)
60 : base(codeNamespace, codeCompileUnit, codeProvider, options, mappings) {}
62 /// <include file='doc\XmlCodeExporter.uex' path='docs/doc[@for="XmlCodeExporter.ExportTypeMapping"]/*' />
64 /// <para>[To be supplied.]</para>
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));
71 ExportElement(xmlTypeMapping.Accessor);
74 /// <include file='doc\XmlCodeExporter.uex' path='docs/doc[@for="XmlCodeExporter.ExportMembersMapping"]/*' />
76 /// <para>[To be supplied.]</para>
78 public void ExportMembersMapping(XmlMembersMapping xmlMembersMapping) {
79 xmlMembersMapping.CheckShallow();
80 CheckScope(xmlMembersMapping.Scope);
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);
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);
94 if (mapping.Text != null) {
95 ExportType(mapping.Text.Mapping, Accessor.UnescapeName(mapping.Text.Name), mapping.Text.Namespace, null, false);
101 void ExportElement(ElementAccessor element) {
102 ExportType(element.Mapping, Accessor.UnescapeName(element.Name), element.Namespace, element, true);
105 void ExportType(TypeMapping mapping, string ns) {
106 ExportType(mapping, null, ns, null, true);
109 void ExportType(TypeMapping mapping, string name, string ns, ElementAccessor rootElement, bool checkReference) {
110 if (mapping.IsReference && mapping.Namespace != Soap.Encoding)
113 if (mapping is StructMapping && checkReference && ((StructMapping)mapping).ReferencedByTopLevelElement && rootElement == null)
116 if (mapping is ArrayMapping && rootElement != null && rootElement.IsTopLevelInSchema && ((ArrayMapping)mapping).TopLevelMapping != null) {
117 mapping = ((ArrayMapping)mapping).TopLevelMapping;
120 CodeTypeDeclaration codeClass = null;
122 if (ExportedMappings[mapping] == null) {
123 ExportedMappings.Add(mapping, mapping);
124 if (mapping.TypeDesc.IsMappedType) {
125 codeClass = mapping.TypeDesc.ExtendedType.ExportTypeDefinition(CodeNamespace, CodeCompileUnit);
127 else if (mapping is EnumMapping) {
128 codeClass = ExportEnum((EnumMapping)mapping, typeof(XmlEnumAttribute));
130 else if (mapping is StructMapping) {
131 codeClass = ExportStruct((StructMapping)mapping);
133 else if (mapping is ArrayMapping) {
134 EnsureTypesExported(((ArrayMapping)mapping).Elements, ns);
136 if (codeClass != null) {
137 if (!mapping.TypeDesc.IsMappedType) {
138 // Add [GeneratedCodeAttribute(Tool=.., Version=..)]
139 codeClass.CustomAttributes.Add(GeneratedCodeAttribute);
141 // Add [SerializableAttribute]
142 codeClass.CustomAttributes.Add(new CodeAttributeDeclaration(typeof(SerializableAttribute).FullName));
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"))}));
150 AddTypeMetadata(codeClass.CustomAttributes, typeof(XmlTypeAttribute), mapping.TypeDesc.Name, Accessor.UnescapeName(mapping.TypeName), mapping.Namespace, mapping.IncludeInSchema);
152 else if (FindAttributeDeclaration(typeof(GeneratedCodeAttribute), codeClass.CustomAttributes) == null) {
153 // Add [GeneratedCodeAttribute(Tool=.., Version=..)]
154 codeClass.CustomAttributes.Add(GeneratedCodeAttribute);
156 ExportedClasses.Add(mapping, codeClass);
160 codeClass = (CodeTypeDeclaration)ExportedClasses[mapping];
162 if (codeClass != null && rootElement != null)
163 AddRootMetadata(codeClass.CustomAttributes, mapping, name, ns, rootElement);
166 void AddRootMetadata(CodeAttributeDeclarationCollection metadata, TypeMapping typeMapping, string name, string ns, ElementAccessor rootElement) {
167 string rootAttrName = typeof(XmlRootAttribute).FullName;
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;
174 CodeAttributeDeclaration attribute = new CodeAttributeDeclaration(rootAttrName);
175 if (typeMapping.TypeDesc.Name != name) {
176 attribute.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression(name)));
179 attribute.Arguments.Add(new CodeAttributeArgument("Namespace", new CodePrimitiveExpression(ns)));
181 if (typeMapping.TypeDesc != null && typeMapping.TypeDesc.IsAmbiguousDataType) {
182 attribute.Arguments.Add(new CodeAttributeArgument("DataType", new CodePrimitiveExpression(typeMapping.TypeDesc.DataType.Name)));
184 if ((object)(rootElement.IsNullable) != null) {
185 attribute.Arguments.Add(new CodeAttributeArgument("IsNullable", new CodePrimitiveExpression((bool)rootElement.IsNullable)));
187 metadata.Add(attribute);
190 CodeAttributeArgument[] GetDefaultValueArguments(PrimitiveMapping mapping, object value, out CodeExpression initExpression) {
191 initExpression = null;
192 if (value == null) return null;
194 CodeExpression valueExpression = null;
195 CodeExpression typeofValue = null;
196 Type type = value.GetType();
197 CodeAttributeArgument[] arguments = null;
199 if (mapping is EnumMapping) {
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));
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);
213 valueExpression = enumRef;
217 valueExpression = new CodeFieldReferenceExpression(new CodeTypeReferenceExpression(mapping.TypeDesc.FullName), (string)value);
219 initExpression = valueExpression;
220 arguments = new CodeAttributeArgument[] {new CodeAttributeArgument(valueExpression)};
222 else if (type == typeof(bool) ||
223 type == typeof(Int32) ||
224 type == typeof(string) ||
225 type == typeof(double)) {
227 initExpression = valueExpression = new CodePrimitiveExpression(value);
228 arguments = new CodeAttributeArgument[] {new CodeAttributeArgument(valueExpression)};
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));
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
246 value = PromoteType(type, value);
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));
253 else if (type == typeof(DateTime)) {
254 DateTime dt = (DateTime)value;
257 if (mapping.TypeDesc.FormatterName == "Date") {
258 dtString = XmlCustomFormatter.FromDate(dt);
259 ticks = (new DateTime(dt.Year, dt.Month, dt.Day)).Ticks;
261 else if (mapping.TypeDesc.FormatterName == "Time") {
262 dtString = XmlCustomFormatter.FromDateTime(dt);
266 dtString = XmlCustomFormatter.FromDateTime(dt);
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)});
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});
281 if (mapping.TypeDesc.FullName != type.ToString() && !(mapping is EnumMapping)) {
283 initExpression = new CodeCastExpression(mapping.TypeDesc.FullName, initExpression);
288 object ImportDefault(TypeMapping mapping, string defaultValue) {
289 if (defaultValue == null)
292 if (mapping.IsList) {
293 string[] vals = defaultValue.Trim().Split(null);
295 // count all non-zero length values;
297 for (int i = 0; i < vals.Length; i++) {
298 if (vals[i] != null && vals[i].Length > 0) count++;
301 object[] values = new object[count];
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]);
310 return ImportDefaultValue(mapping, defaultValue);
313 object ImportDefaultValue(TypeMapping mapping, string defaultValue) {
314 if (defaultValue == null)
316 if (!(mapping is PrimitiveMapping))
319 if (mapping is EnumMapping) {
320 EnumMapping em = (EnumMapping)mapping;
321 ConstantMapping[] c = em.Constants;
324 Hashtable values = new Hashtable();
325 string[] names = new string[c.Length];
326 long[] ids = new long[c.Length];
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]);
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);
338 for (int i = 0; i < c.Length; i++) {
339 if (c[i].XmlName == defaultValue)
343 throw new InvalidOperationException(Res.GetString(Res.XmlInvalidDefaultValue, defaultValue, em.TypeDesc.FullName));
347 PrimitiveMapping pm = (PrimitiveMapping)mapping;
349 if (!pm.TypeDesc.HasCustomFormatter) {
350 if (pm.TypeDesc.FormatterName == "String")
352 if (pm.TypeDesc.FormatterName == "DateTime")
353 return XmlCustomFormatter.ToDateTime(defaultValue);
355 Type formatter = typeof(XmlConvert);
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});
362 Debug.WriteLineIf(DiagnosticsSwitches.XmlSerialization.TraceVerbose, "XmlSerialization::Failed to GetMethod " + formatter.Name + ".To" + pm.TypeDesc.FormatterName);
366 if (pm.TypeDesc.HasDefaultSupport) {
367 return XmlCustomFormatter.ToDefaultValue(defaultValue, pm.TypeDesc.FormatterName);
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()));
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));
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);
397 AddInitializationStatement(ctor, field, init);
400 field.InitExpression = extension.ImportDefaultValue((string)defaultValue, mapping.TypeDesc.FullName);
403 if (comments != null) {
404 DropDefaultAttribute(accessor, comments, mapping.TypeDesc.FullName);
406 AddWarningComment(comments, Res.GetString(Res.XmlNotKnownDefaultValue, extension.GetType().FullName, attributeName, (string)defaultValue, mapping.TypeName, mapping.Namespace));
412 if (defaultValue is string || defaultValue == null) {
413 value = ImportDefault(mapping, (string)defaultValue);
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()));
423 PrimitiveMapping pm = (PrimitiveMapping)mapping;
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);
430 if (value == DBNull.Value) {
431 if (comments != null) {
432 AddWarningComment(comments, Res.GetString(Res.XmlDropAttributeValue, attributeName, pm.TypeName, defaultValue.ToString()));
436 CodeAttributeArgument[] arguments = null;
437 CodeExpression initExpression = null;
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));
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]);
450 initExpression = new CodeArrayCreateExpression(field.Type, initializers);
454 arguments = GetDefaultValueArguments(pm, value, out initExpression);
459 AddInitializationStatement(ctor, field, initExpression);
462 field.InitExpression = initExpression;
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);
470 else if (comments != null) {
471 DropDefaultAttribute(accessor, comments, memberTypeDesc.FullName);
475 static void AddInitializationStatement(CodeConstructor ctor, CodeMemberField field, CodeExpression init) {
476 CodeAssignStatement assign = new CodeAssignStatement();
477 assign.Left = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), field.Name);
479 ctor.Statements.Add(assign);
482 static void DropDefaultAttribute(Accessor accessor, CodeCommentStatementCollection comments, string type) {
483 if (!accessor.IsFixed && accessor.IsOptional) {
484 AddWarningComment(comments, Res.GetString(Res.XmlDropDefaultAttribute, type));
488 CodeTypeDeclaration ExportStruct(StructMapping mapping) {
489 if (mapping.TypeDesc.IsRoot) {
490 ExportRoot(mapping, typeof(XmlIncludeAttribute));
494 string className = mapping.TypeDesc.Name;
495 string baseName = mapping.TypeDesc.BaseTypeDesc == null || mapping.TypeDesc.BaseTypeDesc.IsRoot ? string.Empty : mapping.TypeDesc.BaseTypeDesc.FullName;
497 CodeTypeDeclaration codeClass = new CodeTypeDeclaration(className);
498 codeClass.IsPartial = CodeProvider.Supports(GeneratorSupport.PartialTypes);
499 codeClass.Comments.Add(new CodeCommentStatement(Res.GetString(Res.XmlRemarks), true));
501 CodeNamespace.Types.Add(codeClass);
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;
510 if (baseName != null && baseName.Length > 0) {
511 codeClass.BaseTypes.Add(baseName);
514 AddPropertyChangedNotifier(codeClass);
516 codeClass.TypeAttributes |= TypeAttributes.Public;
517 if (mapping.TypeDesc.IsAbstract) {
518 codeClass.TypeAttributes |= TypeAttributes.Abstract;
521 AddIncludeMetadata(codeClass.CustomAttributes, mapping, typeof(XmlIncludeAttribute));
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++;
532 if (GenerateProperties) {
533 for (int i = 0; i < mapping.Members.Length; i++) {
534 ExportProperty(codeClass, mapping.Members[i], mapping.Namespace, mapping.Scope, ctor);
538 for (int i = 0; i < mapping.Members.Length; i++) {
539 ExportMember(codeClass, mapping.Members[i], mapping.Namespace, ctor);
543 for (int i = 0; i < mapping.Members.Length; i++) {
544 if (mapping.Members[i].Xmlns != null)
546 EnsureTypesExported(mapping.Members[i].Elements, mapping.Namespace);
547 EnsureTypesExported(mapping.Members[i].Attribute, mapping.Namespace);
548 EnsureTypesExported(mapping.Members[i].Text, mapping.Namespace);
551 if (mapping.BaseMapping != null)
552 ExportType(mapping.BaseMapping, null, mapping.Namespace, null, false);
554 ExportDerivedStructs(mapping);
555 CodeGenerator.ValidateIdentifiers(codeClass);
556 if (ctor.Statements.Count == 0) codeClass.Members.Remove(ctor);
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);
566 /// <include file='doc\XmlCodeExporter.uex' path='docs/doc[@for="XmlCodeExporter.AddMappingMetadata"]/*' />
568 /// <para>[To be supplied.]</para>
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);
580 /// <include file='doc\XmlCodeExporter.uex' path='docs/doc[@for="XmlCodeExporter.AddMappingMetadata1"]/*' />
582 /// <para>[To be supplied.]</para>
584 public void AddMappingMetadata(CodeAttributeDeclarationCollection metadata, XmlMemberMapping member, string ns, bool forceUseMemberName) {
585 AddMemberMetadata(null, metadata, member.Mapping, ns, forceUseMemberName, null, null);
588 /// <include file='doc\XmlCodeExporter.uex' path='docs/doc[@for="XmlCodeExporter.AddMappingMetadata2"]/*' />
590 /// <para>[To be supplied.]</para>
592 public void AddMappingMetadata(CodeAttributeDeclarationCollection metadata, XmlMemberMapping member, string ns) {
593 AddMemberMetadata(null, metadata, member.Mapping, ns, false, null, null);
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);
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);
618 else if (member.Attribute != null) {
619 AttributeAccessor attribute = member.Attribute;
621 ExportAnyAttribute(metadata);
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,
635 defaultForm ? XmlSchemaForm.None : attribute.Form);
637 AddDefaultValueAttribute(field, metadata, attribute.Default, mapping, comments, member.TypeDesc, attribute, ctor);
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);
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;
657 ExportAnyElement(metadata, elemName, element.Namespace, member.SequenceId);
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);
668 ExportArrayElements(metadata, array, element.Namespace, member.TypeDesc.ArrayElementTypeDesc, 0);
671 bool sameType = mapping.TypeDesc == member.TypeDesc ||
672 (member.TypeDesc.IsArrayLike && mapping.TypeDesc == member.TypeDesc.ArrayElementTypeDesc);
673 if (member.TypeDesc.IsArrayLike)
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);
677 AddDefaultValueAttribute(field, metadata, element.Default, mapping, comments, member.TypeDesc, element, ctor);
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;
685 ExportAnyElement(metadata, elemName, element.Namespace, member.SequenceId);
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);
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);
698 CodeAttributeDeclaration attribute = new CodeAttributeDeclaration(typeof(XmlIgnoreAttribute).FullName);
699 metadata.Add(attribute);
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);
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);
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);
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);
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);
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);
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)));
753 if (dataType != null) {
754 attribute.Arguments.Add(new CodeAttributeArgument("DataType", new CodePrimitiveExpression(dataType)));
756 metadata.Add(attribute);
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);
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);
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);
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);
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);
778 attribute.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression(name)));
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 + "]")));
786 attribute.Arguments.Add(new CodeAttributeArgument(new CodeTypeOfExpression(typeDesc.FullName)));
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"))));
792 if (form == XmlSchemaForm.Unqualified && ns != null && ns.Length == 0) {
797 attribute.Arguments.Add(new CodeAttributeArgument("Namespace", new CodePrimitiveExpression(ns)));
799 if (dataTypeDesc != null && dataTypeDesc.IsAmbiguousDataType && !dataTypeDesc.IsMappedType) {
800 attribute.Arguments.Add(new CodeAttributeArgument("DataType", new CodePrimitiveExpression(dataTypeDesc.DataType.Name)));
802 if (isNullable != null) {
803 attribute.Arguments.Add(new CodeAttributeArgument("IsNullable", new CodePrimitiveExpression((bool)isNullable)));
805 if (nestingLevel > 0) {
806 attribute.Arguments.Add(new CodeAttributeArgument("NestingLevel", new CodePrimitiveExpression(nestingLevel)));
808 if (sequenceId >= 0) {
809 attribute.Arguments.Add(new CodeAttributeArgument("Order", new CodePrimitiveExpression(sequenceId)));
811 if (attribute.Arguments.Count == 0 && attributeType == typeof(XmlElementAttribute)) return;
812 metadata.Add(attribute);
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)));
821 attribute.Arguments.Add(new CodeAttributeArgument("Namespace", new CodePrimitiveExpression(ns)));
823 if (sequenceId >= 0) {
824 attribute.Arguments.Add(new CodeAttributeArgument("Order", new CodePrimitiveExpression(sequenceId)));
826 metadata.Add(attribute);
829 void ExportAnyAttribute(CodeAttributeDeclarationCollection metadata) {
830 metadata.Add(new CodeAttributeDeclaration(typeof(XmlAnyAttributeAttribute).FullName));
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);
839 void EnsureTypesExported(Accessor accessor, string ns) {
840 if (accessor == null) return;
841 ExportType(accessor.Mapping, null, ns, null, false);