CodeExporter.cs 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380
  1. //------------------------------------------------------------------------------
  2. // <copyright file="CodeExporter.cs" company="Microsoft">
  3. // Copyright (c) Microsoft Corporation. All rights reserved.
  4. // </copyright>
  5. // <owner current="true" primary="true">Microsoft</owner>
  6. //------------------------------------------------------------------------------
  7. namespace System.Xml.Serialization {
  8. using System;
  9. using System.Collections;
  10. using System.IO;
  11. using System.ComponentModel;
  12. using System.Xml.Schema;
  13. using System.CodeDom;
  14. using System.CodeDom.Compiler;
  15. using System.Reflection;
  16. using System.Globalization;
  17. using System.Diagnostics;
  18. using System.Security.Permissions;
  19. /// <include file='doc\CodeExporter.uex' path='docs/doc[@for="CodeExporter"]/*' />
  20. ///<internalonly/>
  21. /// <devdoc>
  22. /// <para>[To be supplied.]</para>
  23. /// </devdoc>
  24. [PermissionSet(SecurityAction.InheritanceDemand, Name="FullTrust")]
  25. public abstract class CodeExporter {
  26. Hashtable exportedMappings;
  27. Hashtable exportedClasses; // TypeMapping -> CodeTypeDeclaration
  28. CodeNamespace codeNamespace;
  29. CodeCompileUnit codeCompileUnit;
  30. bool rootExported;
  31. TypeScope scope;
  32. CodeAttributeDeclarationCollection includeMetadata = new CodeAttributeDeclarationCollection();
  33. CodeGenerationOptions options;
  34. CodeDomProvider codeProvider;
  35. CodeAttributeDeclaration generatedCodeAttribute;
  36. internal CodeExporter(CodeNamespace codeNamespace, CodeCompileUnit codeCompileUnit, CodeDomProvider codeProvider, CodeGenerationOptions options, Hashtable exportedMappings) {
  37. if (codeNamespace != null)
  38. CodeGenerator.ValidateIdentifiers(codeNamespace);
  39. this.codeNamespace = codeNamespace;
  40. if (codeCompileUnit != null) {
  41. if (!codeCompileUnit.ReferencedAssemblies.Contains("System.dll"))
  42. codeCompileUnit.ReferencedAssemblies.Add("System.dll");
  43. if (!codeCompileUnit.ReferencedAssemblies.Contains("System.Xml.dll"))
  44. codeCompileUnit.ReferencedAssemblies.Add("System.Xml.dll");
  45. }
  46. this.codeCompileUnit = codeCompileUnit;
  47. this.options = options;
  48. this.exportedMappings = exportedMappings;
  49. this.codeProvider = codeProvider;
  50. }
  51. internal CodeCompileUnit CodeCompileUnit {
  52. get { return codeCompileUnit; }
  53. }
  54. internal CodeNamespace CodeNamespace {
  55. get {
  56. if (codeNamespace == null)
  57. codeNamespace = new CodeNamespace();
  58. return codeNamespace;
  59. }
  60. }
  61. internal CodeDomProvider CodeProvider {
  62. get {
  63. if (codeProvider == null)
  64. codeProvider = new Microsoft.CSharp.CSharpCodeProvider();
  65. return codeProvider;
  66. }
  67. }
  68. internal Hashtable ExportedClasses {
  69. get {
  70. if (exportedClasses == null)
  71. exportedClasses = new Hashtable();
  72. return exportedClasses;
  73. }
  74. }
  75. internal Hashtable ExportedMappings {
  76. get {
  77. if (exportedMappings == null)
  78. exportedMappings = new Hashtable();
  79. return exportedMappings;
  80. }
  81. }
  82. internal bool GenerateProperties {
  83. get { return (options & CodeGenerationOptions.GenerateProperties) != 0; }
  84. }
  85. internal CodeAttributeDeclaration GeneratedCodeAttribute {
  86. get {
  87. if (generatedCodeAttribute == null) {
  88. CodeAttributeDeclaration decl = new CodeAttributeDeclaration(typeof(GeneratedCodeAttribute).FullName);
  89. Assembly a = Assembly.GetEntryAssembly();
  90. if (a == null) {
  91. a = Assembly.GetExecutingAssembly();
  92. if (a == null) {
  93. a = typeof(CodeExporter).Assembly;
  94. }
  95. }
  96. AssemblyName assemblyName = a.GetName();
  97. decl.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression(assemblyName.Name)));
  98. string version = GetProductVersion(a);
  99. decl.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression(version == null ? assemblyName.Version.ToString() : version)));
  100. generatedCodeAttribute = decl;
  101. }
  102. return generatedCodeAttribute;
  103. }
  104. }
  105. internal static CodeAttributeDeclaration FindAttributeDeclaration(Type type, CodeAttributeDeclarationCollection metadata) {
  106. foreach (CodeAttributeDeclaration attribute in metadata) {
  107. if (attribute.Name == type.FullName || attribute.Name == type.Name) {
  108. return attribute;
  109. }
  110. }
  111. return null;
  112. }
  113. private static string GetProductVersion(Assembly assembly) {
  114. object[] attributes = assembly.GetCustomAttributes(true);
  115. for ( int i = 0; i<attributes.Length; i++ ) {
  116. if (attributes[i] is AssemblyInformationalVersionAttribute) {
  117. AssemblyInformationalVersionAttribute version = (AssemblyInformationalVersionAttribute)attributes[i];
  118. return version.InformationalVersion;
  119. }
  120. }
  121. return null;
  122. }
  123. /// <include file='doc\XmlCodeExporter.uex' path='docs/doc[@for="XmlCodeExporter.IncludeMetadata"]/*' />
  124. /// <devdoc>
  125. /// <para>[To be supplied.]</para>
  126. /// </devdoc>
  127. public CodeAttributeDeclarationCollection IncludeMetadata {
  128. get { return includeMetadata; }
  129. }
  130. internal TypeScope Scope {
  131. get { return scope; }
  132. }
  133. internal void CheckScope(TypeScope scope) {
  134. if (this.scope == null) {
  135. this.scope = scope;
  136. }
  137. else if (this.scope != scope) {
  138. throw new InvalidOperationException(Res.GetString(Res.XmlMappingsScopeMismatch));
  139. }
  140. }
  141. internal abstract void ExportDerivedStructs(StructMapping mapping);
  142. internal abstract void EnsureTypesExported(Accessor[] accessors, string ns);
  143. internal static void AddWarningComment(CodeCommentStatementCollection comments, string text) {
  144. Debug.Assert(comments != null);
  145. comments.Add(new CodeCommentStatement(Res.GetString(Res.XmlCodegenWarningDetails, text), false));
  146. }
  147. internal void ExportRoot(StructMapping mapping, Type includeType) {
  148. if (!rootExported) {
  149. rootExported = true;
  150. ExportDerivedStructs(mapping);
  151. for (StructMapping derived = mapping.DerivedMappings; derived != null; derived = derived.NextDerivedMapping) {
  152. if (!derived.ReferencedByElement && derived.IncludeInSchema && !derived.IsAnonymousType) {
  153. CodeAttributeDeclaration include = new CodeAttributeDeclaration(includeType.FullName);
  154. include.Arguments.Add(new CodeAttributeArgument(new CodeTypeOfExpression(derived.TypeDesc.FullName)));
  155. includeMetadata.Add(include);
  156. }
  157. }
  158. Hashtable typesIncluded = new Hashtable();
  159. foreach (TypeMapping m in Scope.TypeMappings) {
  160. if (m is ArrayMapping) {
  161. ArrayMapping arrayMapping = (ArrayMapping) m;
  162. if (ShouldInclude(arrayMapping) && !typesIncluded.Contains(arrayMapping.TypeDesc.FullName)) {
  163. CodeAttributeDeclaration include = new CodeAttributeDeclaration(includeType.FullName);
  164. include.Arguments.Add(new CodeAttributeArgument(new CodeTypeOfExpression(arrayMapping.TypeDesc.FullName)));
  165. includeMetadata.Add(include);
  166. typesIncluded.Add(arrayMapping.TypeDesc.FullName, string.Empty);
  167. EnsureTypesExported(arrayMapping.Elements, arrayMapping.Namespace);
  168. }
  169. }
  170. }
  171. }
  172. }
  173. private static bool ShouldInclude(ArrayMapping arrayMapping) {
  174. if (arrayMapping.ReferencedByElement)
  175. return false;
  176. if (arrayMapping.Next != null)
  177. return false;
  178. if (arrayMapping.Elements.Length == 1) {
  179. TypeKind kind = arrayMapping.Elements[0].Mapping.TypeDesc.Kind;
  180. if (kind == TypeKind.Node)
  181. return false;
  182. }
  183. for (int i = 0; i < arrayMapping.Elements.Length; i++) {
  184. if (arrayMapping.Elements[i].Name != arrayMapping.Elements[i].Mapping.DefaultElementName) {
  185. // in the case we need custom attributes to serialize an array instance, we cannot include arrau mapping without explicit reference.
  186. return false;
  187. }
  188. }
  189. return true;
  190. }
  191. internal CodeTypeDeclaration ExportEnum(EnumMapping mapping, Type type) {
  192. CodeTypeDeclaration codeClass = new CodeTypeDeclaration(mapping.TypeDesc.Name);
  193. codeClass.Comments.Add(new CodeCommentStatement(Res.GetString(Res.XmlRemarks), true));
  194. codeClass.IsEnum = true;
  195. if (mapping.IsFlags && mapping.Constants.Length > 31) {
  196. codeClass.BaseTypes.Add(new CodeTypeReference(typeof(long)));
  197. }
  198. codeClass.TypeAttributes |= TypeAttributes.Public;
  199. CodeNamespace.Types.Add(codeClass);
  200. for (int i = 0; i < mapping.Constants.Length; i++) {
  201. ExportConstant(codeClass, mapping.Constants[i], type, mapping.IsFlags, 1L << i);
  202. }
  203. if (mapping.IsFlags) {
  204. // Add [FlagsAttribute]
  205. CodeAttributeDeclaration flags = new CodeAttributeDeclaration(typeof(FlagsAttribute).FullName);
  206. codeClass.CustomAttributes.Add(flags);
  207. }
  208. CodeGenerator.ValidateIdentifiers(codeClass);
  209. return codeClass;
  210. }
  211. internal void AddTypeMetadata(CodeAttributeDeclarationCollection metadata, Type type, string defaultName, string name, string ns, bool includeInSchema) {
  212. CodeAttributeDeclaration attribute = new CodeAttributeDeclaration(type.FullName);
  213. if (name == null || name.Length == 0) {
  214. attribute.Arguments.Add(new CodeAttributeArgument("AnonymousType", new CodePrimitiveExpression(true)));
  215. }
  216. else {
  217. if (defaultName != name) {
  218. attribute.Arguments.Add(new CodeAttributeArgument("TypeName", new CodePrimitiveExpression(name)));
  219. }
  220. }
  221. if (ns != null && ns.Length != 0) {
  222. attribute.Arguments.Add(new CodeAttributeArgument("Namespace", new CodePrimitiveExpression(ns)));
  223. }
  224. if (!includeInSchema) {
  225. attribute.Arguments.Add(new CodeAttributeArgument("IncludeInSchema", new CodePrimitiveExpression(false)));
  226. }
  227. if (attribute.Arguments.Count > 0) {
  228. metadata.Add(attribute);
  229. }
  230. }
  231. internal static void AddIncludeMetadata(CodeAttributeDeclarationCollection metadata, StructMapping mapping, Type type) {
  232. if (mapping.IsAnonymousType)
  233. return;
  234. for (StructMapping derived = mapping.DerivedMappings; derived != null; derived = derived.NextDerivedMapping) {
  235. CodeAttributeDeclaration attribute = new CodeAttributeDeclaration(type.FullName);
  236. attribute.Arguments.Add(new CodeAttributeArgument(new CodeTypeOfExpression(derived.TypeDesc.FullName)));
  237. metadata.Add(attribute);
  238. AddIncludeMetadata(metadata, derived, type);
  239. }
  240. }
  241. internal static void ExportConstant(CodeTypeDeclaration codeClass, ConstantMapping constant, Type type, bool init, long enumValue) {
  242. CodeMemberField field = new CodeMemberField(typeof(int).FullName, constant.Name);
  243. field.Comments.Add(new CodeCommentStatement(Res.GetString(Res.XmlRemarks), true));
  244. if (init)
  245. field.InitExpression = new CodePrimitiveExpression(enumValue);
  246. codeClass.Members.Add(field);
  247. if (constant.XmlName != constant.Name) {
  248. CodeAttributeDeclaration attribute = new CodeAttributeDeclaration(type.FullName);
  249. attribute.Arguments.Add(new CodeAttributeArgument(new CodePrimitiveExpression(constant.XmlName)));
  250. field.CustomAttributes.Add(attribute);
  251. }
  252. }
  253. internal static object PromoteType(Type type, object value) {
  254. if (type == typeof(sbyte)) {
  255. return ((IConvertible)value).ToInt16(null);
  256. }
  257. else if (type == typeof(UInt16)) {
  258. return ((IConvertible)value).ToInt32(null);
  259. }
  260. else if (type == typeof(UInt32)) {
  261. return ((IConvertible)value).ToInt64(null);
  262. }
  263. else if (type == typeof(UInt64)) {
  264. return ((IConvertible)value).ToDecimal(null);
  265. }
  266. else {
  267. return value;
  268. }
  269. }
  270. internal CodeMemberProperty CreatePropertyDeclaration(CodeMemberField field, string name, string typeName) {
  271. CodeMemberProperty prop = new CodeMemberProperty();
  272. prop.Type = new CodeTypeReference(typeName);
  273. prop.Name = name;
  274. prop.Attributes = (prop.Attributes & ~MemberAttributes.AccessMask) | MemberAttributes.Public;
  275. //add get
  276. CodeMethodReturnStatement ret = new CodeMethodReturnStatement();
  277. ret.Expression = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), field.Name);
  278. prop.GetStatements.Add(ret);
  279. CodeAssignStatement propertySet = new CodeAssignStatement();
  280. CodeExpression left = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), field.Name);
  281. CodeExpression right = new CodePropertySetValueReferenceExpression();
  282. propertySet.Left = left;
  283. propertySet.Right = right;
  284. if (EnableDataBinding)
  285. {
  286. prop.SetStatements.Add(propertySet);
  287. prop.SetStatements.Add(new CodeMethodInvokeExpression(new CodeThisReferenceExpression(), RaisePropertyChangedEventMethod.Name, new CodePrimitiveExpression(name)));
  288. }
  289. else
  290. prop.SetStatements.Add(propertySet);
  291. return prop;
  292. }
  293. internal static string MakeFieldName(string name) {
  294. return CodeIdentifier.MakeCamel(name) + "Field";
  295. }
  296. internal void AddPropertyChangedNotifier(CodeTypeDeclaration codeClass)
  297. {
  298. if (EnableDataBinding && codeClass != null)
  299. {
  300. if (codeClass.BaseTypes.Count == 0)
  301. {
  302. codeClass.BaseTypes.Add(typeof(object));
  303. }
  304. codeClass.BaseTypes.Add(new CodeTypeReference(typeof(System.ComponentModel.INotifyPropertyChanged)));
  305. codeClass.Members.Add(PropertyChangedEvent);
  306. codeClass.Members.Add(RaisePropertyChangedEventMethod);
  307. }
  308. }
  309. bool EnableDataBinding {
  310. get { return (options & CodeGenerationOptions.EnableDataBinding) != 0; }
  311. }
  312. internal static CodeMemberMethod RaisePropertyChangedEventMethod
  313. {
  314. get
  315. {
  316. CodeMemberMethod raisePropertyChangedEventMethod = new CodeMemberMethod();
  317. raisePropertyChangedEventMethod.Name = "RaisePropertyChanged";
  318. raisePropertyChangedEventMethod.Attributes = MemberAttributes.Family | MemberAttributes.Final;
  319. CodeArgumentReferenceExpression propertyName = new CodeArgumentReferenceExpression("propertyName");
  320. raisePropertyChangedEventMethod.Parameters.Add(new CodeParameterDeclarationExpression(typeof(string), propertyName.ParameterName));
  321. CodeVariableReferenceExpression propertyChanged = new CodeVariableReferenceExpression("propertyChanged");
  322. raisePropertyChangedEventMethod.Statements.Add(new CodeVariableDeclarationStatement(typeof(PropertyChangedEventHandler), propertyChanged.VariableName, new CodeEventReferenceExpression(new CodeThisReferenceExpression(), PropertyChangedEvent.Name)));
  323. CodeConditionStatement ifStatement = new CodeConditionStatement(new CodeBinaryOperatorExpression(propertyChanged, CodeBinaryOperatorType.IdentityInequality, new CodePrimitiveExpression(null)));
  324. raisePropertyChangedEventMethod.Statements.Add(ifStatement);
  325. ifStatement.TrueStatements.Add(new CodeDelegateInvokeExpression(propertyChanged, new CodeThisReferenceExpression(), new CodeObjectCreateExpression(typeof(PropertyChangedEventArgs), propertyName)));
  326. return raisePropertyChangedEventMethod;
  327. }
  328. }
  329. internal static CodeMemberEvent PropertyChangedEvent
  330. {
  331. get
  332. {
  333. CodeMemberEvent propertyChangedEvent = new CodeMemberEvent();
  334. propertyChangedEvent.Attributes = MemberAttributes.Public;
  335. propertyChangedEvent.Name = "PropertyChanged";
  336. propertyChangedEvent.Type = new CodeTypeReference(typeof(PropertyChangedEventHandler));
  337. propertyChangedEvent.ImplementationTypes.Add(typeof(System.ComponentModel.INotifyPropertyChanged));
  338. return propertyChangedEvent;
  339. }
  340. }
  341. }
  342. }