XmlCodeExporter.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473
  1. //
  2. // System.Xml.Serialization.XmlCodeExporter
  3. //
  4. // Author:
  5. // Tim Coleman ([email protected])
  6. // Lluis Sanchez Gual ([email protected])
  7. //
  8. // Copyright (C) Tim Coleman, 2002
  9. //
  10. using System.CodeDom;
  11. using System.Collections;
  12. using System.Xml.Schema;
  13. namespace System.Xml.Serialization {
  14. public class XmlCodeExporter {
  15. #region Fields
  16. CodeNamespace codeNamespace;
  17. CodeCompileUnit codeCompileUnit;
  18. CodeAttributeDeclarationCollection includeMetadata;
  19. Hashtable exportedMaps = new Hashtable ();
  20. #endregion
  21. #region Constructors
  22. public XmlCodeExporter (CodeNamespace codeNamespace)
  23. {
  24. includeMetadata = new CodeAttributeDeclarationCollection ();
  25. this.codeNamespace = codeNamespace;
  26. }
  27. public XmlCodeExporter (CodeNamespace codeNamespace, CodeCompileUnit codeCompileUnit)
  28. : this (codeNamespace)
  29. {
  30. this.codeCompileUnit = codeCompileUnit;
  31. }
  32. #endregion // Constructors
  33. #region Properties
  34. public CodeAttributeDeclarationCollection IncludeMetadata {
  35. get { return includeMetadata; }
  36. }
  37. #endregion Properties
  38. #region Methods
  39. public void AddMappingMetadata (CodeAttributeDeclarationCollection metadata, XmlMemberMapping member, string ns)
  40. {
  41. AddMappingMetadata (metadata, member, ns, false);
  42. }
  43. [MonoTODO]
  44. public void AddMappingMetadata (CodeAttributeDeclarationCollection metadata, XmlTypeMapping member, string ns)
  45. {
  46. throw new NotImplementedException ();
  47. }
  48. public void AddMappingMetadata (CodeAttributeDeclarationCollection metadata, XmlMemberMapping member, string ns, bool forceUseMemberName)
  49. {
  50. CodeAttributeDeclaration att;
  51. TypeData memType = member.TypeMapMember.TypeData;
  52. if (memType.SchemaType == SchemaTypes.Array)
  53. {
  54. // Array parameter
  55. att = new CodeAttributeDeclaration ("System.Xml.Serialization.XmlArray");
  56. if (forceUseMemberName || (member.ElementName != member.MemberName)) att.Arguments.Add (GetArg ("ElementName", member.ElementName));
  57. if (member.Namespace != ns) att.Arguments.Add (GetArg ("Namespace", member.Namespace));
  58. if (att.Arguments.Count > 0) metadata.Add (att);
  59. }
  60. else
  61. {
  62. att = new CodeAttributeDeclaration ("System.Xml.Serialization.XmlElement");
  63. if (forceUseMemberName || (member.ElementName != member.MemberName)) att.Arguments.Add (GetArg ("ElementName", member.ElementName));
  64. if (member.Namespace != ns) att.Arguments.Add (GetArg ("Namespace", member.Namespace));
  65. if (!TypeTranslator.IsDefaultPrimitiveTpeData (memType)) att.Arguments.Add (GetArg ("DataType", member.TypeName));
  66. if (att.Arguments.Count > 0) metadata.Add (att);
  67. }
  68. }
  69. public void ExportMembersMapping (XmlMembersMapping xmlMembersMapping)
  70. {
  71. CodeTypeDeclaration dummyClass = new CodeTypeDeclaration ();
  72. ExportMembersMapCode (dummyClass, (ClassMap)xmlMembersMapping.ObjectMap, xmlMembersMapping.Namespace, null);
  73. }
  74. public void ExportTypeMapping (XmlTypeMapping xmlTypeMapping)
  75. {
  76. ExportMapCode (xmlTypeMapping, true);
  77. }
  78. void ExportMapCode (XmlTypeMapping map, bool isRoot)
  79. {
  80. switch (map.TypeData.SchemaType)
  81. {
  82. case SchemaTypes.Enum:
  83. ExportEnumCode (map);
  84. break;
  85. case SchemaTypes.Array:
  86. ExportArrayCode (map);
  87. break;
  88. case SchemaTypes.Class:
  89. ExportClassCode (map, isRoot);
  90. break;
  91. case SchemaTypes.XmlSerializable:
  92. case SchemaTypes.XmlNode:
  93. case SchemaTypes.Primitive:
  94. // Ignore
  95. break;
  96. }
  97. }
  98. void ExportClassCode (XmlTypeMapping map, bool isRoot)
  99. {
  100. if (IsMapExported (map)) return;
  101. SetMapExported (map);
  102. CodeTypeDeclaration codeClass = new CodeTypeDeclaration (map.TypeData.TypeName);
  103. AddCodeType (codeClass, map.Documentation);
  104. codeClass.Attributes = MemberAttributes.Public;
  105. CodeAttributeDeclaration att = new CodeAttributeDeclaration ("System.Xml.Serialization.XmlType");
  106. if (map.XmlType != map.TypeData.TypeName) att.Arguments.Add (GetArg (map.XmlType));
  107. if (map.XmlTypeNamespace != "") att.Arguments.Add (GetArg ("Namespace", map.XmlTypeNamespace));
  108. AddCustomAttribute (codeClass, att, false);
  109. if (map.ElementName != map.XmlType || map.Namespace != map.XmlTypeNamespace) {
  110. CodeAttributeDeclaration ratt = new CodeAttributeDeclaration ("System.Xml.Serialization.XmlRoot");
  111. ratt.Arguments.Add (GetArg (map.ElementName));
  112. ratt.Arguments.Add (GetArg ("Namespace", map.Namespace));
  113. AddCustomAttribute (codeClass, ratt, false);
  114. }
  115. ExportMembersMapCode (codeClass, (ClassMap)map.ObjectMap, map.Namespace, map.BaseMap);
  116. if (map.BaseMap != null)
  117. {
  118. CodeTypeReference ctr = new CodeTypeReference (map.BaseMap.TypeData.FullTypeName);
  119. codeClass.BaseTypes.Add (ctr);
  120. ExportMapCode (map.BaseMap, false);
  121. }
  122. ExportDerivedTypes (map, codeClass);
  123. }
  124. void ExportDerivedTypes (XmlTypeMapping map, CodeTypeDeclaration codeClass)
  125. {
  126. foreach (XmlTypeMapping tm in map.DerivedTypes)
  127. {
  128. CodeAttributeDeclaration iatt = new CodeAttributeDeclaration ("System.Xml.Serialization.XmlInclude");
  129. iatt.Arguments.Add (new CodeAttributeArgument (new CodeTypeOfExpression(tm.TypeData.FullTypeName)));
  130. AddCustomAttribute (codeClass, iatt, true);
  131. ExportMapCode (tm, false);
  132. ExportDerivedTypes (tm, codeClass);
  133. }
  134. }
  135. void ExportMembersMapCode (CodeTypeDeclaration codeClass, ClassMap map, string defaultNamespace, XmlTypeMapping baseMap)
  136. {
  137. ICollection members = map.ElementMembers;
  138. if (members != null)
  139. {
  140. foreach (XmlTypeMapMemberElement member in members)
  141. {
  142. if (baseMap != null && DefinedInBaseMap (baseMap, member)) continue;
  143. Type memType = member.GetType();
  144. if (memType == typeof(XmlTypeMapMemberList))
  145. {
  146. AddArrayElementFieldMember (codeClass, (XmlTypeMapMemberList) member, defaultNamespace);
  147. }
  148. else if (memType == typeof(XmlTypeMapMemberFlatList))
  149. {
  150. AddElementFieldMember (codeClass, member, defaultNamespace);
  151. }
  152. else if (memType == typeof(XmlTypeMapMemberAnyElement))
  153. {
  154. AddAnyElementFieldMember (codeClass, member, defaultNamespace);
  155. }
  156. else if (memType == typeof(XmlTypeMapMemberElement))
  157. {
  158. AddElementFieldMember (codeClass, member, defaultNamespace);
  159. }
  160. else
  161. {
  162. throw new InvalidOperationException ("Member type " + memType + " not supported");
  163. }
  164. }
  165. }
  166. // Write attributes
  167. ICollection attributes = map.AttributeMembers;
  168. if (attributes != null)
  169. {
  170. foreach (XmlTypeMapMemberAttribute attr in attributes) {
  171. if (baseMap != null && DefinedInBaseMap (baseMap, attr)) continue;
  172. AddAttributeFieldMember (codeClass, attr, defaultNamespace);
  173. }
  174. }
  175. XmlTypeMapMember anyAttrMember = map.DefaultAnyAttributeMember;
  176. if (anyAttrMember != null)
  177. {
  178. CodeMemberField codeField = new CodeMemberField (anyAttrMember.TypeData.FullTypeName, anyAttrMember.Name);
  179. AddComments (codeField, anyAttrMember.Documentation);
  180. codeField.Attributes = MemberAttributes.Public;
  181. AddCustomAttribute (codeField, "System.Xml.Serialization.XmlAnyAttribute");
  182. codeClass.Members.Add (codeField);
  183. }
  184. }
  185. CodeMemberField CreateFieldMember (string type, string name, object defaultValue, string comments)
  186. {
  187. CodeMemberField codeField = new CodeMemberField (type, name);
  188. codeField.Attributes = MemberAttributes.Public;
  189. AddComments (codeField, comments);
  190. if (defaultValue != System.DBNull.Value)
  191. {
  192. AddCustomAttribute (codeField, "System.ComponentModel.DefaultValue", GetArg (defaultValue));
  193. codeField.InitExpression = new CodePrimitiveExpression (defaultValue);
  194. }
  195. return codeField;
  196. }
  197. void AddAttributeFieldMember (CodeTypeDeclaration codeClass, XmlTypeMapMemberAttribute attinfo, string defaultNamespace)
  198. {
  199. CodeMemberField codeField = CreateFieldMember (attinfo.TypeData.FullTypeName, attinfo.Name, attinfo.DefaultValue, attinfo.Documentation);
  200. codeClass.Members.Add (codeField);
  201. CodeAttributeDeclaration att = new CodeAttributeDeclaration ("System.Xml.Serialization.XmlAttribute");
  202. if (attinfo.Name != attinfo.AttributeName) att.Arguments.Add (GetArg (attinfo.AttributeName));
  203. if (attinfo.Namespace != defaultNamespace) att.Arguments.Add (GetArg ("Namespace", attinfo.Namespace));
  204. if (attinfo.Form != XmlSchemaForm.None) att.Arguments.Add (GetArg ("Form",attinfo.Form));
  205. if (!TypeTranslator.IsDefaultPrimitiveTpeData(attinfo.TypeData)) att.Arguments.Add (GetArg ("DataType",attinfo.TypeData.XmlType));
  206. AddCustomAttribute (codeField, att, true);
  207. if (attinfo.MappedType != null)
  208. ExportMapCode (attinfo.MappedType, false);
  209. }
  210. void AddElementFieldMember (CodeTypeDeclaration codeClass, XmlTypeMapMemberElement member, string defaultNamespace)
  211. {
  212. CodeMemberField codeField = CreateFieldMember (member.TypeData.FullTypeName, member.Name, member.DefaultValue, member.Documentation);
  213. codeClass.Members.Add (codeField);
  214. TypeData defaultType = member.TypeData;
  215. bool addAlwaysAttr = false;
  216. if (member is XmlTypeMapMemberFlatList)
  217. {
  218. defaultType = defaultType.ListItemTypeData;
  219. addAlwaysAttr = true;
  220. }
  221. foreach (XmlTypeMapElementInfo einfo in member.ElementInfo)
  222. {
  223. if (ExportExtraElementAttributes (codeField, einfo, defaultNamespace, defaultType))
  224. continue;
  225. CodeAttributeDeclaration att = new CodeAttributeDeclaration ("System.Xml.Serialization.XmlElement");
  226. if (einfo.ElementName != member.Name) att.Arguments.Add (GetArg (einfo.ElementName));
  227. if (einfo.TypeData.FullTypeName != defaultType.FullTypeName) att.Arguments.Add (GetTypeArg ("Type", einfo.TypeData.FullTypeName));
  228. if (einfo.Namespace != defaultNamespace) att.Arguments.Add (GetArg ("Namespace", einfo.Namespace));
  229. if (einfo.IsNullable) att.Arguments.Add (GetArg ("IsNullable", true));
  230. if (!TypeTranslator.IsDefaultPrimitiveTpeData(einfo.TypeData)) att.Arguments.Add (GetArg ("DataType",einfo.TypeData.XmlType));
  231. AddCustomAttribute (codeField, att, addAlwaysAttr);
  232. if (einfo.MappedType != null) ExportMapCode (einfo.MappedType, false);
  233. }
  234. if (member.ChoiceMember != null)
  235. AddCustomAttribute (codeField, "System.Xml.Serialization.XmlChoiceIdentifier", GetArg(member.ChoiceMember));
  236. }
  237. void AddAnyElementFieldMember (CodeTypeDeclaration codeClass, XmlTypeMapMemberElement member, string defaultNamespace)
  238. {
  239. CodeMemberField codeField = CreateFieldMember (member.TypeData.FullTypeName, member.Name, member.DefaultValue, member.Documentation);
  240. codeClass.Members.Add (codeField);
  241. foreach (XmlTypeMapElementInfo einfo in member.ElementInfo)
  242. {
  243. ExportExtraElementAttributes (codeField, einfo, defaultNamespace, einfo.TypeData);
  244. }
  245. }
  246. bool DefinedInBaseMap (XmlTypeMapping map, XmlTypeMapMember member)
  247. {
  248. if (((ClassMap)map.ObjectMap).FindMember (member.Name) != null)
  249. return true;
  250. else if (map.BaseMap != null)
  251. return DefinedInBaseMap (map.BaseMap, member);
  252. else
  253. return false;
  254. }
  255. void AddArrayElementFieldMember (CodeTypeDeclaration codeClass, XmlTypeMapMemberList member, string defaultNamespace)
  256. {
  257. CodeMemberField codeField = new CodeMemberField (member.TypeData.FullTypeName, member.Name);
  258. AddComments (codeField, member.Documentation);
  259. codeField.Attributes = MemberAttributes.Public;
  260. codeClass.Members.Add (codeField);
  261. XmlTypeMapElementInfo einfo = (XmlTypeMapElementInfo) member.ElementInfo[0];
  262. CodeAttributeDeclaration att = new CodeAttributeDeclaration ("System.Xml.Serialization.XmlArray");
  263. if (einfo.ElementName != member.Name) att.Arguments.Add (GetArg ("ElementName", einfo.ElementName));
  264. if (einfo.Namespace != defaultNamespace) att.Arguments.Add (GetArg ("Namespace", einfo.Namespace));
  265. if (einfo.IsNullable) att.Arguments.Add (GetArg ("IsNullable", true));
  266. AddCustomAttribute (codeField, att, false);
  267. ListMap listMap = (ListMap) member.ListTypeMapping.ObjectMap;
  268. AddArrayItemAttributes (codeField, listMap, member.TypeData.ListItemTypeData, defaultNamespace, 0);
  269. }
  270. void AddArrayItemAttributes (CodeMemberField codeField, ListMap listMap, TypeData type, string defaultNamespace, int nestingLevel)
  271. {
  272. foreach (XmlTypeMapElementInfo ainfo in listMap.ItemInfo)
  273. {
  274. string defaultName;
  275. if (ainfo.MappedType != null) defaultName = ainfo.MappedType.ElementName;
  276. else defaultName = ainfo.TypeData.XmlType;
  277. bool needsType = (listMap.ItemInfo.Count > 1) ||
  278. (ainfo.TypeData.FullTypeName != type.FullTypeName && !listMap.IsMultiArray);
  279. CodeAttributeDeclaration att = new CodeAttributeDeclaration ("System.Xml.Serialization.XmlArrayItem");
  280. if (ainfo.ElementName != defaultName) att.Arguments.Add (GetArg ("ElementName", ainfo.ElementName));
  281. if (ainfo.Namespace != defaultNamespace && ainfo.Namespace != XmlSchema.Namespace) att.Arguments.Add (GetArg ("Namespace", ainfo.Namespace));
  282. if (needsType) att.Arguments.Add (GetTypeArg ("Type", ainfo.TypeData.FullTypeName));
  283. if (ainfo.IsNullable) att.Arguments.Add (GetArg ("IsNullable", true));
  284. if (att.Arguments.Count > 0 && nestingLevel > 0) att.Arguments.Add (GetArg ("NestingLevel", nestingLevel));
  285. AddCustomAttribute (codeField, att, false);
  286. if (ainfo.MappedType != null) ExportMapCode (ainfo.MappedType, false);
  287. }
  288. if (listMap.IsMultiArray)
  289. {
  290. XmlTypeMapping nmap = listMap.NestedArrayMapping;
  291. AddArrayItemAttributes (codeField, (ListMap) nmap.ObjectMap, nmap.TypeData.ListItemTypeData, defaultNamespace, nestingLevel + 1);
  292. }
  293. }
  294. void ExportArrayCode (XmlTypeMapping map)
  295. {
  296. ListMap listMap = (ListMap) map.ObjectMap;
  297. foreach (XmlTypeMapElementInfo ainfo in listMap.ItemInfo)
  298. {
  299. if (ainfo.MappedType != null) ExportMapCode (ainfo.MappedType, false);
  300. }
  301. }
  302. bool ExportExtraElementAttributes (CodeMemberField codeField, XmlTypeMapElementInfo einfo, string defaultNamespace, TypeData defaultType)
  303. {
  304. if (einfo.IsTextElement) {
  305. CodeAttributeDeclaration uatt = new CodeAttributeDeclaration ("System.Xml.Serialization.XmlText");
  306. if (einfo.TypeData.FullTypeName != defaultType.FullTypeName) uatt.Arguments.Add (GetTypeArg ("Type", einfo.TypeData.FullTypeName));
  307. AddCustomAttribute (codeField, uatt, true);
  308. return true;
  309. }
  310. else if (einfo.IsUnnamedAnyElement) {
  311. CodeAttributeDeclaration uatt = new CodeAttributeDeclaration ("System.Xml.Serialization.XmlAnyElement");
  312. if (!einfo.IsUnnamedAnyElement) uatt.Arguments.Add (GetArg ("Name", einfo.ElementName));
  313. if (einfo.Namespace != defaultNamespace) uatt.Arguments.Add (GetArg ("Namespace", einfo.Namespace));
  314. AddCustomAttribute (codeField, uatt, true);
  315. return true;
  316. }
  317. return false;
  318. }
  319. void ExportEnumCode (XmlTypeMapping map)
  320. {
  321. if (IsMapExported (map)) return;
  322. SetMapExported (map);
  323. CodeTypeDeclaration codeEnum = new CodeTypeDeclaration (map.TypeData.TypeName);
  324. codeEnum.Attributes = MemberAttributes.Public;
  325. codeEnum.IsEnum = true;
  326. AddCodeType (codeEnum, map.Documentation);
  327. CodeAttributeDeclaration att = new CodeAttributeDeclaration ("System.Xml.Serialization.XmlTypeAttribute");
  328. if (map.ElementName != map.TypeData.TypeName) att.Arguments.Add (GetArg ("Name", map.ElementName));
  329. if (map.Namespace != "") att.Arguments.Add (GetArg ("Namespace", map.Namespace));
  330. AddCustomAttribute (codeEnum, att, false);
  331. EnumMap emap = (EnumMap) map.ObjectMap;
  332. foreach (EnumMap.EnumMapMember emem in emap.Members)
  333. {
  334. CodeMemberField codeField = new CodeMemberField ("", emem.EnumName);
  335. AddComments (codeField, emem.Documentation);
  336. if (emem.EnumName != emem.XmlName)
  337. {
  338. CodeAttributeDeclaration xatt = new CodeAttributeDeclaration ("System.Xml.Serialization.XmlEnum");
  339. xatt.Arguments.Add (GetArg ("Name", emem.XmlName));
  340. AddCustomAttribute (codeField, xatt, true);
  341. }
  342. codeEnum.Members.Add (codeField);
  343. }
  344. }
  345. bool IsMapExported (XmlTypeMapping map)
  346. {
  347. if (exportedMaps.Contains (map)) return true;
  348. if (map.TypeData.Type == typeof(object)) return true;
  349. if (!map.IncludeInSchema) return true;
  350. return false;
  351. }
  352. void SetMapExported (XmlTypeMapping map)
  353. {
  354. exportedMaps.Add (map,map);
  355. }
  356. void AddCustomAttribute (CodeTypeMember ctm, CodeAttributeDeclaration att, bool addIfNoParams)
  357. {
  358. if (att.Arguments.Count == 0 && !addIfNoParams) return;
  359. if (ctm.CustomAttributes == null) ctm.CustomAttributes = new CodeAttributeDeclarationCollection ();
  360. ctm.CustomAttributes.Add (att);
  361. }
  362. void AddCustomAttribute (CodeTypeMember ctm, string name, params CodeAttributeArgument[] args)
  363. {
  364. if (ctm.CustomAttributes == null) ctm.CustomAttributes = new CodeAttributeDeclarationCollection ();
  365. ctm.CustomAttributes.Add (new CodeAttributeDeclaration (name, args));
  366. }
  367. CodeAttributeArgument GetArg (string name, object value)
  368. {
  369. return new CodeAttributeArgument (name, new CodePrimitiveExpression(value));
  370. }
  371. CodeAttributeArgument GetArg (object value)
  372. {
  373. return new CodeAttributeArgument (new CodePrimitiveExpression(value));
  374. }
  375. CodeAttributeArgument GetTypeArg (string name, string typeName)
  376. {
  377. return new CodeAttributeArgument (name, new CodeTypeOfExpression(typeName));
  378. }
  379. void AddComments (CodeTypeMember member, string comments)
  380. {
  381. if (comments == null || comments == "") member.Comments.Add (new CodeCommentStatement ("<remarks/>", true));
  382. else member.Comments.Add (new CodeCommentStatement ("<remarks>\n" + comments + "\n</remarks>", true));
  383. }
  384. void AddCodeType (CodeTypeDeclaration type, string comments)
  385. {
  386. AddComments (type, comments);
  387. codeNamespace.Types.Add (type);
  388. }
  389. #endregion // Methods
  390. }
  391. }