2 // System.Xml.Serialization.MapCodeGenerator
5 // Lluis Sanchez Gual (lluis@ximian.com)
7 // Copyright (C) Ximian, Inc., 2003
11 // Permission is hereby granted, free of charge, to any person obtaining
12 // a copy of this software and associated documentation files (the
13 // "Software"), to deal in the Software without restriction, including
14 // without limitation the rights to use, copy, modify, merge, publish,
15 // distribute, sublicense, and/or sell copies of the Software, and to
16 // permit persons to whom the Software is furnished to do so, subject to
17 // the following conditions:
19 // The above copyright notice and this permission notice shall be
20 // included in all copies or substantial portions of the Software.
22 // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
23 // EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
24 // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
25 // NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 // LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
27 // OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
28 // WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
32 using System.CodeDom.Compiler;
33 using System.Collections;
34 using System.Globalization;
35 using System.Xml.Schema;
37 namespace System.Xml.Serialization {
38 internal class MapCodeGenerator {
40 CodeNamespace codeNamespace;
41 CodeCompileUnit codeCompileUnit;
42 CodeAttributeDeclarationCollection includeMetadata;
43 XmlTypeMapping exportedAnyType = null;
44 protected bool includeArrayTypes;
45 ICodeGenerator codeGen;
46 CodeGenerationOptions options;
48 Hashtable exportedMaps = new Hashtable ();
49 Hashtable includeMaps = new Hashtable ();
51 public MapCodeGenerator (CodeNamespace codeNamespace, CodeCompileUnit codeCompileUnit)
53 this.codeCompileUnit = codeCompileUnit;
54 this.codeNamespace = codeNamespace;
55 this.options = CodeGenerationOptions.GenerateOldAsync;
58 public MapCodeGenerator (CodeNamespace codeNamespace, CodeCompileUnit codeCompileUnit, ICodeGenerator codeGen, CodeGenerationOptions options, Hashtable mappings)
60 this.codeCompileUnit = codeCompileUnit;
61 this.codeNamespace = codeNamespace;
62 this.codeGen = codeGen;
63 this.options = options;
64 // this.mappings = mappings;
67 public CodeAttributeDeclarationCollection IncludeMetadata
71 if (includeMetadata != null) return includeMetadata;
72 includeMetadata = new CodeAttributeDeclarationCollection ();
74 foreach (XmlTypeMapping map in includeMaps.Values)
75 GenerateClassInclude (includeMetadata, map);
77 return includeMetadata;
81 #region Code generation methods
83 public void ExportMembersMapping (XmlMembersMapping xmlMembersMapping)
85 CodeTypeDeclaration dummyClass = new CodeTypeDeclaration ();
86 ExportMembersMapCode (dummyClass, (ClassMap)xmlMembersMapping.ObjectMap, xmlMembersMapping.Namespace, null);
89 public void ExportTypeMapping (XmlTypeMapping xmlTypeMapping)
91 ExportMapCode (xmlTypeMapping);
92 RemoveInclude (xmlTypeMapping);
95 void ExportMapCode (XmlTypeMapping map)
97 switch (map.TypeData.SchemaType)
99 case SchemaTypes.Enum:
100 ExportEnumCode (map);
103 case SchemaTypes.Array:
104 ExportArrayCode (map);
107 case SchemaTypes.Class:
108 ExportClassCode (map);
111 case SchemaTypes.XmlSerializable:
112 case SchemaTypes.XmlNode:
113 case SchemaTypes.Primitive:
119 void ExportClassCode (XmlTypeMapping map)
121 CodeTypeDeclaration codeClass;
123 if (IsMapExported (map)) {
124 // Regenerate attributes, since things may have changed
125 codeClass = GetMapDeclaration (map);
126 if (codeClass != null) {
127 codeClass.CustomAttributes = null;
128 GenerateClass (map, codeClass);
129 ExportDerivedTypeAttributes (map, codeClass);
134 if (map.TypeData.Type == typeof(object))
136 exportedAnyType = map;
137 SetMapExported (map, null);
138 foreach (XmlTypeMapping dmap in exportedAnyType.DerivedTypes) {
139 if (IsMapExported (dmap) || !dmap.IncludeInSchema) continue;
140 ExportTypeMapping (dmap);
146 codeClass = new CodeTypeDeclaration (map.TypeData.TypeName);
147 SetMapExported (map, codeClass);
149 AddCodeType (codeClass, map.Documentation);
150 codeClass.Attributes = MemberAttributes.Public;
152 GenerateClass (map, codeClass);
153 ExportDerivedTypeAttributes (map, codeClass);
155 ExportMembersMapCode (codeClass, (ClassMap)map.ObjectMap, map.XmlTypeNamespace, map.BaseMap);
157 if (map.BaseMap != null && map.BaseMap.TypeData.SchemaType != SchemaTypes.XmlNode)
159 CodeTypeReference ctr = new CodeTypeReference (map.BaseMap.TypeData.FullTypeName);
160 codeClass.BaseTypes.Add (ctr);
161 if (map.BaseMap.IncludeInSchema) {
162 ExportMapCode (map.BaseMap);
163 AddInclude (map.BaseMap);
166 ExportDerivedTypes (map, codeClass);
169 void ExportDerivedTypeAttributes (XmlTypeMapping map, CodeTypeDeclaration codeClass)
171 foreach (XmlTypeMapping tm in map.DerivedTypes)
173 GenerateClassInclude (codeClass.CustomAttributes, tm);
174 ExportDerivedTypeAttributes (tm, codeClass);
178 void ExportDerivedTypes (XmlTypeMapping map, CodeTypeDeclaration codeClass)
180 foreach (XmlTypeMapping tm in map.DerivedTypes)
182 if (codeClass.CustomAttributes == null)
183 codeClass.CustomAttributes = new CodeAttributeDeclarationCollection ();
186 ExportDerivedTypes (tm, codeClass);
190 void ExportMembersMapCode (CodeTypeDeclaration codeClass, ClassMap map, string defaultNamespace, XmlTypeMapping baseMap)
192 ICollection members = map.ElementMembers;
196 foreach (XmlTypeMapMemberElement member in members)
198 if (baseMap != null && DefinedInBaseMap (baseMap, member)) continue;
200 Type memType = member.GetType();
201 if (memType == typeof(XmlTypeMapMemberList))
203 AddArrayElementFieldMember (codeClass, (XmlTypeMapMemberList) member, defaultNamespace);
205 else if (memType == typeof(XmlTypeMapMemberFlatList))
207 AddElementFieldMember (codeClass, member, defaultNamespace);
209 else if (memType == typeof(XmlTypeMapMemberAnyElement))
211 AddAnyElementFieldMember (codeClass, member, defaultNamespace);
213 else if (memType == typeof(XmlTypeMapMemberElement))
215 AddElementFieldMember (codeClass, member, defaultNamespace);
219 throw new InvalidOperationException ("Member type " + memType + " not supported");
226 ICollection attributes = map.AttributeMembers;
227 if (attributes != null)
229 foreach (XmlTypeMapMemberAttribute attr in attributes) {
230 if (baseMap != null && DefinedInBaseMap (baseMap, attr)) continue;
231 AddAttributeFieldMember (codeClass, attr, defaultNamespace);
235 XmlTypeMapMember anyAttrMember = map.DefaultAnyAttributeMember;
236 if (anyAttrMember != null)
238 CodeTypeMember codeField = CreateFieldMember (codeClass, anyAttrMember.TypeData, anyAttrMember.Name);
239 AddComments (codeField, anyAttrMember.Documentation);
240 codeField.Attributes = MemberAttributes.Public;
241 GenerateAnyAttribute (codeField);
245 CodeTypeMember CreateFieldMember (CodeTypeDeclaration codeClass, Type type, string name)
247 return CreateFieldMember (codeClass, new CodeTypeReference(type), name, System.DBNull.Value, null, null);
250 CodeTypeMember CreateFieldMember (CodeTypeDeclaration codeClass, TypeData type, string name)
252 return CreateFieldMember (codeClass, GetDomType (type), name, System.DBNull.Value, null, null);
255 CodeTypeMember CreateFieldMember (CodeTypeDeclaration codeClass, XmlTypeMapMember member)
257 return CreateFieldMember (codeClass, GetDomType (member.TypeData), member.Name, member.DefaultValue, member.TypeData, member.Documentation);
260 CodeTypeMember CreateFieldMember (CodeTypeDeclaration codeClass, CodeTypeReference type, string name, object defaultValue, TypeData defaultType, string documentation)
262 CodeMemberField codeField = null;
263 CodeTypeMember codeProp = null;
265 if ((options & CodeGenerationOptions.GenerateProperties) > 0) {
266 string field = CodeIdentifier.MakeCamel (name + "Field");
267 codeField = new CodeMemberField (type, field);
268 codeField.Attributes = MemberAttributes.Private;
269 codeClass.Members.Add (codeField);
271 CodeMemberProperty prop = new CodeMemberProperty ();
274 prop.Attributes = MemberAttributes.Public | MemberAttributes.Final;
276 prop.HasGet = prop.HasSet = true;
278 CodeExpression ce = new CodeFieldReferenceExpression (new CodeThisReferenceExpression(), field);
279 prop.SetStatements.Add (new CodeAssignStatement (ce, new CodePropertySetValueReferenceExpression()));
280 prop.GetStatements.Add (new CodeMethodReturnStatement (ce));
283 codeField = new CodeMemberField (type, name);
284 codeField.Attributes = MemberAttributes.Public;
285 codeProp = codeField;
288 if (defaultValue != System.DBNull.Value)
289 GenerateDefaultAttribute (codeField, codeProp, defaultType, defaultValue);
291 AddComments (codeProp, documentation);
292 codeClass.Members.Add (codeProp);
296 void AddAttributeFieldMember (CodeTypeDeclaration codeClass, XmlTypeMapMemberAttribute attinfo, string defaultNamespace)
298 CodeTypeMember codeField = CreateFieldMember (codeClass, attinfo);
300 CodeAttributeDeclarationCollection attributes = codeField.CustomAttributes;
301 if (attributes == null) attributes = new CodeAttributeDeclarationCollection ();
303 GenerateAttributeMember (attributes, attinfo, defaultNamespace, false);
304 if (attributes.Count > 0) codeField.CustomAttributes = attributes;
306 if (attinfo.MappedType != null) {
307 ExportMapCode (attinfo.MappedType);
308 RemoveInclude (attinfo.MappedType);
311 if (attinfo.TypeData.IsValueType && attinfo.IsOptionalValueType)
313 codeField = CreateFieldMember (codeClass, typeof(bool), attinfo.Name + "Specified");
314 codeField.Attributes = MemberAttributes.Public;
315 GenerateSpecifierMember (codeField);
319 public void AddAttributeMemberAttributes (XmlTypeMapMemberAttribute attinfo, string defaultNamespace, CodeAttributeDeclarationCollection attributes, bool forceUseMemberName)
321 GenerateAttributeMember (attributes, attinfo, defaultNamespace, forceUseMemberName);
324 void AddElementFieldMember (CodeTypeDeclaration codeClass, XmlTypeMapMemberElement member, string defaultNamespace)
326 CodeTypeMember codeField = CreateFieldMember (codeClass, member);
328 CodeAttributeDeclarationCollection attributes = codeField.CustomAttributes;
329 if (attributes == null) attributes = new CodeAttributeDeclarationCollection ();
331 AddElementMemberAttributes (member, defaultNamespace, attributes, false);
332 if (attributes.Count > 0) codeField.CustomAttributes = attributes;
334 if (member.TypeData.IsValueType && member.IsOptionalValueType)
336 codeField = CreateFieldMember (codeClass, typeof(bool), member.Name + "Specified");
337 codeField.Attributes = MemberAttributes.Public;
338 GenerateSpecifierMember (codeField);
342 public void AddElementMemberAttributes (XmlTypeMapMemberElement member, string defaultNamespace, CodeAttributeDeclarationCollection attributes, bool forceUseMemberName)
344 TypeData defaultType = member.TypeData;
345 bool addAlwaysAttr = false;
347 if (member is XmlTypeMapMemberFlatList)
349 defaultType = defaultType.ListItemTypeData;
350 addAlwaysAttr = true;
353 foreach (XmlTypeMapElementInfo einfo in member.ElementInfo)
355 if (ExportExtraElementAttributes (attributes, einfo, defaultNamespace, defaultType))
358 GenerateElementInfoMember (attributes, member, einfo, defaultType, defaultNamespace, addAlwaysAttr, forceUseMemberName);
359 if (einfo.MappedType != null) {
360 ExportMapCode (einfo.MappedType);
361 RemoveInclude (einfo.MappedType);
365 GenerateElementMember (attributes, member);
368 void AddAnyElementFieldMember (CodeTypeDeclaration codeClass, XmlTypeMapMemberElement member, string defaultNamespace)
370 CodeTypeMember codeField = CreateFieldMember (codeClass, member);
372 CodeAttributeDeclarationCollection attributes = new CodeAttributeDeclarationCollection ();
373 foreach (XmlTypeMapElementInfo einfo in member.ElementInfo)
374 ExportExtraElementAttributes (attributes, einfo, defaultNamespace, einfo.TypeData);
376 if (attributes.Count > 0) codeField.CustomAttributes = attributes;
379 bool DefinedInBaseMap (XmlTypeMapping map, XmlTypeMapMember member)
381 if (((ClassMap)map.ObjectMap).FindMember (member.Name) != null)
383 else if (map.BaseMap != null)
384 return DefinedInBaseMap (map.BaseMap, member);
389 void AddArrayElementFieldMember (CodeTypeDeclaration codeClass, XmlTypeMapMemberList member, string defaultNamespace)
391 CodeTypeMember codeField = CreateFieldMember (codeClass, member.TypeData, member.Name);
392 codeField.Attributes = MemberAttributes.Public;
394 CodeAttributeDeclarationCollection attributes = new CodeAttributeDeclarationCollection ();
395 AddArrayAttributes (attributes, member, defaultNamespace, false);
397 ListMap listMap = (ListMap) member.ListTypeMapping.ObjectMap;
398 AddArrayItemAttributes (attributes, listMap, member.TypeData.ListItemTypeData, defaultNamespace, 0);
400 if (attributes.Count > 0) codeField.CustomAttributes = attributes;
403 public void AddArrayAttributes (CodeAttributeDeclarationCollection attributes, XmlTypeMapMemberElement member, string defaultNamespace, bool forceUseMemberName)
405 GenerateArrayElement (attributes, member, defaultNamespace, forceUseMemberName);
408 public void AddArrayItemAttributes (CodeAttributeDeclarationCollection attributes, ListMap listMap, TypeData type, string defaultNamespace, int nestingLevel)
410 foreach (XmlTypeMapElementInfo ainfo in listMap.ItemInfo)
413 if (ainfo.MappedType != null) defaultName = ainfo.MappedType.ElementName;
414 else defaultName = ainfo.TypeData.XmlType;
416 GenerateArrayItemAttributes (attributes, listMap, type, ainfo, defaultName, defaultNamespace, nestingLevel);
417 if (ainfo.MappedType != null) {
418 if (!IsMapExported (ainfo.MappedType) && includeArrayTypes)
419 AddInclude (ainfo.MappedType);
420 ExportMapCode (ainfo.MappedType);
424 if (listMap.IsMultiArray)
426 XmlTypeMapping nmap = listMap.NestedArrayMapping;
427 AddArrayItemAttributes (attributes, (ListMap) nmap.ObjectMap, nmap.TypeData.ListItemTypeData, defaultNamespace, nestingLevel + 1);
431 void ExportArrayCode (XmlTypeMapping map)
433 ListMap listMap = (ListMap) map.ObjectMap;
434 foreach (XmlTypeMapElementInfo ainfo in listMap.ItemInfo)
436 if (ainfo.MappedType != null) {
437 if (!IsMapExported (ainfo.MappedType) && includeArrayTypes)
438 AddInclude (ainfo.MappedType);
439 ExportMapCode (ainfo.MappedType);
444 bool ExportExtraElementAttributes (CodeAttributeDeclarationCollection attributes, XmlTypeMapElementInfo einfo, string defaultNamespace, TypeData defaultType)
446 if (einfo.IsTextElement) {
447 GenerateTextElementAttribute (attributes, einfo, defaultType);
450 else if (einfo.IsUnnamedAnyElement) {
451 GenerateUnnamedAnyElementAttribute (attributes, einfo, defaultNamespace);
457 void ExportEnumCode (XmlTypeMapping map)
459 if (IsMapExported (map)) return;
461 CodeTypeDeclaration codeEnum = new CodeTypeDeclaration (map.TypeData.TypeName);
462 SetMapExported (map, codeEnum);
464 codeEnum.Attributes = MemberAttributes.Public;
465 codeEnum.IsEnum = true;
466 AddCodeType (codeEnum, map.Documentation);
468 GenerateEnum (map, codeEnum);
469 EnumMap emap = (EnumMap) map.ObjectMap;
472 codeEnum.CustomAttributes.Add (new CodeAttributeDeclaration ("System.Flags"));
475 foreach (EnumMap.EnumMapMember emem in emap.Members)
477 CodeMemberField codeField = new CodeMemberField ("", emem.EnumName);
479 codeField.InitExpression = new CodePrimitiveExpression (flag);
483 AddComments (codeField, emem.Documentation);
485 GenerateEnumItem (codeField, emem);
486 codeEnum.Members.Add (codeField);
490 void AddInclude (XmlTypeMapping map)
492 if (!includeMaps.ContainsKey (map.TypeData.FullTypeName))
493 includeMaps [map.TypeData.FullTypeName] = map;
496 void RemoveInclude (XmlTypeMapping map)
498 includeMaps.Remove (map.TypeData.FullTypeName);
503 #region Helper methods
505 bool IsMapExported (XmlTypeMapping map)
507 if (exportedMaps.Contains (map.TypeData.FullTypeName)) return true;
511 void SetMapExported (XmlTypeMapping map, CodeTypeDeclaration declaration)
513 exportedMaps.Add (map.TypeData.FullTypeName, declaration);
516 CodeTypeDeclaration GetMapDeclaration (XmlTypeMapping map)
518 return exportedMaps [map.TypeData.FullTypeName] as CodeTypeDeclaration;
521 public static void AddCustomAttribute (CodeTypeMember ctm, CodeAttributeDeclaration att, bool addIfNoParams)
523 if (att.Arguments.Count == 0 && !addIfNoParams) return;
525 if (ctm.CustomAttributes == null) ctm.CustomAttributes = new CodeAttributeDeclarationCollection ();
526 ctm.CustomAttributes.Add (att);
529 public static void AddCustomAttribute (CodeTypeMember ctm, string name, params CodeAttributeArgument[] args)
531 if (ctm.CustomAttributes == null) ctm.CustomAttributes = new CodeAttributeDeclarationCollection ();
532 ctm.CustomAttributes.Add (new CodeAttributeDeclaration (name, args));
535 public static CodeAttributeArgument GetArg (string name, object value)
537 return new CodeAttributeArgument (name, new CodePrimitiveExpression(value));
540 public static CodeAttributeArgument GetArg (object value)
542 return new CodeAttributeArgument (new CodePrimitiveExpression(value));
545 public static CodeAttributeArgument GetTypeArg (string name, string typeName)
547 return new CodeAttributeArgument (name, new CodeTypeOfExpression(typeName));
550 public static CodeAttributeArgument GetEnumArg (string name, string enumType, string enumValue)
552 return new CodeAttributeArgument (name, new CodeFieldReferenceExpression (new CodeTypeReferenceExpression(enumType), enumValue));
555 public static void AddComments (CodeTypeMember member, string comments)
557 if (comments == null || comments == "") member.Comments.Add (new CodeCommentStatement ("<remarks/>", true));
558 else member.Comments.Add (new CodeCommentStatement ("<remarks>\n" + comments + "\n</remarks>", true));
561 void AddCodeType (CodeTypeDeclaration type, string comments)
563 AddComments (type, comments);
564 codeNamespace.Types.Add (type);
567 CodeTypeReference GetDomType (TypeData data)
569 if (data.SchemaType == SchemaTypes.Array)
570 return new CodeTypeReference (GetDomType (data.ListItemTypeData),1);
572 return new CodeTypeReference (data.FullTypeName);
577 #region Overridable methods
579 protected virtual void GenerateClass (XmlTypeMapping map, CodeTypeDeclaration codeClass)
583 protected virtual void GenerateClassInclude (CodeAttributeDeclarationCollection attributes, XmlTypeMapping map)
587 protected virtual void GenerateAnyAttribute (CodeTypeMember codeField)
591 protected virtual void GenerateDefaultAttribute (CodeMemberField internalField, CodeTypeMember externalField, TypeData typeData, object defaultValue)
593 if (typeData.Type == null)
595 // It must be an enumeration defined in the schema.
596 if (typeData.SchemaType != SchemaTypes.Enum)
597 throw new InvalidOperationException ("Type " + typeData.TypeName + " not supported");
599 IFormattable defaultValueFormattable = defaultValue as IFormattable;
600 CodeFieldReferenceExpression fref = new CodeFieldReferenceExpression (new CodeTypeReferenceExpression (typeData.FullTypeName), defaultValueFormattable != null ? defaultValueFormattable.ToString(null, CultureInfo.InvariantCulture) : defaultValue.ToString ());
601 CodeAttributeArgument arg = new CodeAttributeArgument (fref);
602 AddCustomAttribute (externalField, "System.ComponentModel.DefaultValue", arg);
603 internalField.InitExpression = fref;
607 AddCustomAttribute (externalField, "System.ComponentModel.DefaultValue", GetArg (defaultValue));
608 internalField.InitExpression = new CodePrimitiveExpression (defaultValue);
612 protected virtual void GenerateAttributeMember (CodeAttributeDeclarationCollection attributes, XmlTypeMapMemberAttribute attinfo, string defaultNamespace, bool forceUseMemberName)
616 protected virtual void GenerateElementInfoMember (CodeAttributeDeclarationCollection attributes, XmlTypeMapMemberElement member, XmlTypeMapElementInfo einfo, TypeData defaultType, string defaultNamespace, bool addAlwaysAttr, bool forceUseMemberName)
620 protected virtual void GenerateElementMember (CodeAttributeDeclarationCollection attributes, XmlTypeMapMemberElement member)
624 protected virtual void GenerateArrayElement (CodeAttributeDeclarationCollection attributes, XmlTypeMapMemberElement member, string defaultNamespace, bool forceUseMemberName)
628 protected virtual void GenerateArrayItemAttributes (CodeAttributeDeclarationCollection attributes, ListMap listMap, TypeData type, XmlTypeMapElementInfo ainfo, string defaultName, string defaultNamespace, int nestingLevel)
632 protected virtual void GenerateTextElementAttribute (CodeAttributeDeclarationCollection attributes, XmlTypeMapElementInfo einfo, TypeData defaultType)
636 protected virtual void GenerateUnnamedAnyElementAttribute (CodeAttributeDeclarationCollection attributes, XmlTypeMapElementInfo einfo, string defaultNamespace)
640 protected virtual void GenerateEnum (XmlTypeMapping map, CodeTypeDeclaration codeEnum)
644 protected virtual void GenerateEnumItem (CodeMemberField codeField, EnumMap.EnumMapMember emem)
648 protected virtual void GenerateSpecifierMember (CodeTypeMember codeField)