From: Daniel C. \(kzu\) <dca...@us...> - 2004-10-21 20:42:17
|
Update of /cvsroot/mvp-xml/Design/v1/src/CustomTools/XGen In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv28388/v1/src/CustomTools/XGen Added Files: ClassPicker.cs ClassPicker.resx XGenRunner.cs XGenTool.cs XmlSerializerGenerator.cs Log Message: --- NEW FILE: ClassPicker.cs --- using System; using System.Drawing; using System.Collections; using System.ComponentModel; using System.Windows.Forms; namespace Mvp.Xml.Design.CustomTools.XGen { /// <summary> /// Summary description for ClassPicker. /// </summary> public class ClassPicker : System.Windows.Forms.Form { private System.Windows.Forms.Label label1; private System.Windows.Forms.Button btnCancel; private System.Windows.Forms.Button btnAccept; private System.Windows.Forms.CheckedListBox lstClasses; /// <summary> /// Required designer variable. /// </summary> private System.ComponentModel.Container components = null; public ClassPicker() { // // Required for Windows Form Designer support // InitializeComponent(); // // TODO: Add any constructor code after InitializeComponent call // } /// <summary> /// Clean up any resources being used. /// </summary> protected override void Dispose( bool disposing ) { if( disposing ) { if(components != null) { components.Dispose(); } } base.Dispose( disposing ); } #region Windows Form Designer generated code /// <summary> /// Required method for Designer support - do not modify /// the contents of this method with the code editor. /// </summary> private void InitializeComponent() { this.lstClasses = new System.Windows.Forms.CheckedListBox(); this.label1 = new System.Windows.Forms.Label(); this.btnCancel = new System.Windows.Forms.Button(); this.btnAccept = new System.Windows.Forms.Button(); this.SuspendLayout(); // // lstClasses // this.lstClasses.Location = new System.Drawing.Point(8, 56); this.lstClasses.Name = "lstClasses"; this.lstClasses.Size = new System.Drawing.Size(384, 214); this.lstClasses.TabIndex = 0; // // label1 // this.label1.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) | System.Windows.Forms.AnchorStyles.Left) | System.Windows.Forms.AnchorStyles.Right))); this.label1.Location = new System.Drawing.Point(8, 8); this.label1.Name = "label1"; this.label1.Size = new System.Drawing.Size(384, 48); this.label1.TabIndex = 1; this.label1.Text = "Select the classes you will use as the root of a deserialization process. A custo" + "m XmlSerializer and supporting classes will be generated for each of the selecte" + "d ones:"; // // btnCancel // this.btnCancel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.btnCancel.Location = new System.Drawing.Point(316, 280); this.btnCancel.Name = "btnCancel"; this.btnCancel.TabIndex = 2; this.btnCancel.Text = "&Cancel"; // // btnAccept // this.btnAccept.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); this.btnAccept.Location = new System.Drawing.Point(236, 280); this.btnAccept.Name = "btnAccept"; this.btnAccept.TabIndex = 3; this.btnAccept.Text = "&Accept"; // // ClassPicker // this.AutoScaleBaseSize = new System.Drawing.Size(5, 13); this.ClientSize = new System.Drawing.Size(400, 309); this.Controls.Add(this.btnAccept); this.Controls.Add(this.btnCancel); this.Controls.Add(this.label1); this.Controls.Add(this.lstClasses); this.Name = "ClassPicker"; this.StartPosition = System.Windows.Forms.FormStartPosition.CenterParent; this.Text = " Class Picker"; this.ResumeLayout(false); } #endregion } } --- NEW FILE: XGenRunner.cs --- using System; using System.IO; namespace Mvp.Xml.Design.CustomTools.XGen { /// <summary> /// Class that performs the actual code generation in the isolated design domain. /// </summary> internal class XGenRunner : MarshalByRefObject { /// <summary> /// Generates the code for the received type. /// </summary> public XGenRunner(string outputFile, string forType, string targetNamespace) { using (StreamWriter writer = new StreamWriter(outputFile)) { writer.Write(XmlSerializerGenerator.GenerateCode( Type.GetType(forType), targetNamespace)); } } } } --- NEW FILE: ClassPicker.resx --- <?xml version="1.0" encoding="utf-8"?> <root> <!-- Microsoft ResX Schema Version 1.3 The primary goals of this format is to allow a simple XML format that is mostly human readable. The generation and parsing of the various data types are done through the TypeConverter classes associated with the data types. Example: ... ado.net/XML headers & schema ... <resheader name="resmimetype">text/microsoft-resx</resheader> <resheader name="version">1.3</resheader> <resheader name="reader">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader> <resheader name="writer">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader> <data name="Name1">this is my long string</data> <data name="Color1" type="System.Drawing.Color, System.Drawing">Blue</data> <data name="Bitmap1" mimetype="application/x-microsoft.net.object.binary.base64"> [base64 mime encoded serialized .NET Framework object] </data> <data name="Icon1" type="System.Drawing.Icon, System.Drawing" mimetype="application/x-microsoft.net.object.bytearray.base64"> [base64 mime encoded string representing a byte array form of the .NET Framework object] </data> There are any number of "resheader" rows that contain simple name/value pairs. Each data row contains a name, and value. The row also contains a type or mimetype. Type corresponds to a .NET class that support text/value conversion through the TypeConverter architecture. Classes that don't support this are serialized and stored with the mimetype set. The mimetype is used forserialized objects, and tells the ResXResourceReader how to depersist the object. This is currently not extensible. For a given mimetype the value must be set accordingly: Note - application/x-microsoft.net.object.binary.base64 is the format that the ResXResourceWriter will generate, however the reader can read any of the formats listed below. mimetype: application/x-microsoft.net.object.binary.base64 value : The object must be serialized with : System.Serialization.Formatters.Binary.BinaryFormatter : and then encoded with base64 encoding. mimetype: application/x-microsoft.net.object.soap.base64 value : The object must be serialized with : System.Runtime.Serialization.Formatters.Soap.SoapFormatter : and then encoded with base64 encoding. mimetype: application/x-microsoft.net.object.bytearray.base64 value : The object must be serialized into a byte array : using a System.ComponentModel.TypeConverter : and then encoded with base64 encoding. --> <xsd:schema id="root" xmlns="" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"> <xsd:element name="root" msdata:IsDataSet="true"> <xsd:complexType> <xsd:choice maxOccurs="unbounded"> <xsd:element name="data"> <xsd:complexType> <xsd:sequence> <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> <xsd:element name="comment" type="xsd:string" minOccurs="0" msdata:Ordinal="2" /> </xsd:sequence> <xsd:attribute name="name" type="xsd:string" msdata:Ordinal="1" /> <xsd:attribute name="type" type="xsd:string" msdata:Ordinal="3" /> <xsd:attribute name="mimetype" type="xsd:string" msdata:Ordinal="4" /> </xsd:complexType> </xsd:element> <xsd:element name="resheader"> <xsd:complexType> <xsd:sequence> <xsd:element name="value" type="xsd:string" minOccurs="0" msdata:Ordinal="1" /> </xsd:sequence> <xsd:attribute name="name" type="xsd:string" use="required" /> </xsd:complexType> </xsd:element> </xsd:choice> </xsd:complexType> </xsd:element> </xsd:schema> <resheader name="resmimetype"> <value>text/microsoft-resx</value> </resheader> <resheader name="version"> <value>1.3</value> </resheader> <resheader name="reader"> <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> </resheader> <resheader name="writer"> <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value> </resheader> <data name="lstClasses.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <value>Private</value> </data> <data name="lstClasses.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <value>False</value> </data> <data name="lstClasses.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <value>Private</value> </data> <data name="label1.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <value>False</value> </data> <data name="label1.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <value>Private</value> </data> <data name="label1.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <value>Private</value> </data> <data name="btnCancel.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <value>False</value> </data> <data name="btnCancel.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <value>Private</value> </data> <data name="btnCancel.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <value>Private</value> </data> <data name="btnAccept.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <value>False</value> </data> <data name="btnAccept.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <value>Private</value> </data> <data name="btnAccept.Modifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <value>Private</value> </data> <data name="$this.Locked" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <value>False</value> </data> <data name="$this.Language" type="System.Globalization.CultureInfo, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <value>(Default)</value> </data> <data name="$this.TrayLargeIcon" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <value>False</value> </data> <data name="$this.Localizable" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <value>False</value> </data> <data name="$this.GridSize" type="System.Drawing.Size, System.Drawing, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"> <value>4, 4</value> </data> <data name="$this.DrawGrid" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <value>True</value> </data> <data name="$this.TrayHeight" type="System.Int32, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <value>80</value> </data> <data name="$this.Name"> <value>ClassPicker</value> </data> <data name="$this.SnapToGrid" type="System.Boolean, mscorlib, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <value>True</value> </data> <data name="$this.DefaultModifiers" type="System.CodeDom.MemberAttributes, System, Version=1.0.5000.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"> <value>Private</value> </data> </root> --- NEW FILE: XGenTool.cs --- #region using using System; using System.Collections; using System.ComponentModel; using System.ComponentModel.Design; using System.IO; using System.Reflection; using System.Runtime.InteropServices; using System.Text; using System.Xml; using System.Xml.Schema; using EnvDTE; using VSLangProj; #endregion namespace Mvp.Xml.Design.CustomTools.XGen { /// <summary> /// Generates custom typed XmlSerializers. /// </summary> /// <remarks> /// On any class set the Custom Tool property to "SGen". /// This tool supports C# projects only, as that's the code generated /// by the XmlSerializer class. /// </remarks> [Guid("983A6445-3277-4cd1-97DF-5AA11A537921")] [CustomTool("Mvp.Xml.XGen", "MVP XML XmlSerializer Generation Tool", false)] [ComVisible(true)] [VersionSupport("7.1")] [CategorySupport(CategorySupportAttribute.CSharpCategory)] public class XGenTool : CustomTool { static string ConfigFile; #region Static config initialization static XGenTool() { AssemblyName name = Assembly.GetExecutingAssembly().GetName(); ConfigFile = Path.GetTempFileName() + ".config"; using (StreamWriter sw = new StreamWriter(ConfigFile)) { // Keep serialization files. Required for SGen to work. sw.Write(@"<?xml version='1.0' encoding='utf-8' ?> <configuration> <system.diagnostics> <switches> <add name='XmlSerialization.Compilation' value='4'/> </switches> </system.diagnostics> </configuration>"); } } #endregion Static config initialization #region GenerateCode /// <summary> /// Generates the output. /// </summary> protected override byte[] GenerateCode(string inputFileName, string inputFileContent) { AppDomain domain = null; try { // Force compilation of the current project. We need the type in the output. ProjectItem item = base.CurrentItem; item.DTE.Solution.SolutionBuild.BuildProject( item.DTE.Solution.SolutionBuild.ActiveConfiguration.Name, item.ContainingProject.UniqueName, true); // Copy Design assembly to output for the isolated AppDomain. string output = item.ContainingProject.ConfigurationManager.ActiveConfiguration.Properties.Item("OutputPath").Value.ToString(); output = Path.Combine(item.ContainingProject.Properties.Item("FullPath").Value.ToString(), output); string asmfile = ReflectionHelper.GetAssemblyPath(Assembly.GetExecutingAssembly()); File.Copy(asmfile, Path.Combine(output, Path.GetFileName(asmfile)), true); CodeClass targetclass = null; foreach (CodeElement element in item.FileCodeModel.CodeElements) { if (element is CodeNamespace) { foreach (CodeElement codec in ((CodeNamespace)element).Members) { if (codec is CodeClass) { targetclass = (CodeClass) codec; break; } } } else if (element is CodeClass) { targetclass = (CodeClass) element; } if (targetclass != null) { break; } } if (targetclass == null) { return System.Text.Encoding.ASCII.GetBytes( SR.XGenTool_NoClassFound); } string codefile = Path.GetTempFileName(); AppDomainSetup setup = new AppDomainSetup(); setup.ApplicationName = "Mvp.Xml Design Domain"; setup.ApplicationBase = output; setup.ConfigurationFile = ConfigFile; domain = AppDomain.CreateDomain("Mvp.Xml Design Domain", null, setup); // Runner ctor will dump the output to the file we pass. domain.CreateInstance( Assembly.GetExecutingAssembly().FullName, typeof(XGenRunner).FullName, false, 0, null, new object[] { codefile, targetclass.FullName + ", " + item.ContainingProject.Properties.Item("AssemblyName").Value.ToString(), base.FileNameSpace }, null, null, null); string code; using (StreamReader reader = new StreamReader(codefile)) { code = reader.ReadToEnd(); } return System.Text.Encoding.ASCII.GetBytes(code); } catch (Exception e) { return System.Text.Encoding.ASCII.GetBytes(SR.XGenTool_GeneralError(e)); } finally { if (domain != null) { AppDomain.Unload(domain); } } } #endregion GenerateCode #region GetDefaultExtension /// <summary> /// This tool generates code, and the default extension equals that of the current code provider. /// </summary> public override string GetDefaultExtension() { return "Serialization." + base.CodeProvider.FileExtension; } #endregion GetDefaultExtension #region Registration and Installation /// <summary> /// Registers the generator. /// </summary> [ComRegisterFunction] public static void RegisterClass(Type type) { CustomTool.Register(typeof(XGenTool)); } /// <summary> /// Unregisters the generator. /// </summary> [ComUnregisterFunction] public static void UnregisterClass(Type t) { CustomTool.UnRegister(typeof(XGenTool)); } #endregion Registration and Installation } } --- NEW FILE: XmlSerializerGenerator.cs --- #region using using System; using System.Collections; using System.CodeDom; using System.CodeDom.Compiler; using System.IO; using System.Reflection; using System.Text.RegularExpressions; using System.Xml.Serialization; #endregion using namespace Mvp.Xml.Design.CustomTools.XGen { /// <summary> /// Generates source code for custom XmlSerializer for a type. /// </summary> public class XmlSerializerGenerator { #region Consts & Fields // Parses private methods. static Regex PrivateMethods = new Regex(@" # Returned type # (?<return>[^\s]+)\s # Method call start # (?<method> (?<name>Read\d+_ # Object being read # (?<object>[^\(]+))\( # Method arguments # (?<arguments>[^\)]+) # Method call end # \))\s{", RegexOptions.IgnorePatternWhitespace | RegexOptions.Compiled | RegexOptions.Multiline); /// <summary> /// The return type of the read method. /// </summary> const string ReadMethodReturnType = "return"; /// <summary> /// Full method signature excluding return type. /// </summary> const string ReadMethodFullSignature = "method"; /// <summary> /// Method name. /// </summary> const string ReadMethodName = "name"; /// <summary> /// Method arguments. /// </summary> const string ReadMethodArguments = "arguments"; /// <summary> /// Name of the object being read. /// </summary> const string ReadMethodObjectName = "object"; /// <summary> /// Removes the type declaration from a list of method arguments retrieved by <see cref="PrivateMethods"/>. /// </summary> static Regex RemoveTypeFromArguments = new Regex(@"[^\s,]+[\s]", RegexOptions.Compiled); static Regex PublicRead = new Regex(@"public object (?<method>Read\d+_(?<object>[^\(]+))", RegexOptions.Compiled | RegexOptions.Multiline); /// <summary> /// The root object name of the element to (de)serialize. /// </summary> const string PublicRootObject = "object"; /// <summary> /// The name of the public read method. /// </summary> const string PublicMethodName = "method"; static Regex PublicWrite = new Regex(@"public void (?<method>Write\d+_(?<object>[^\(]+))", RegexOptions.Compiled | RegexOptions.Multiline); #region Code templates /// <summary> /// Template for the override for each element being read. /// {0}=Return type /// {1}=Method name /// {2}=Delegate name /// {3}=Event name /// {4}=Arguments with type definition /// {5}=Arguments with no type /// </summary> const string TemplateMethodOverride = @" /// <remarks/> protected override {0} {1}({4}) {{ {0} obj = base.{1}({5}); {2} handler = {3}; if (handler != null) handler(obj); return obj; }}"; /// <summary> /// Template for the public Read method for the root object /// in the custom reader firing events. /// {0}=Return type /// {1}=Read method name generated by serializer for root object. /// </summary> const string TemplateReadMethod = @" /// <summary>Reads an object of type {0}.</summary> internal {0} Read() {{ return ({0}) {1}(); }}"; /// <summary> /// Template for the public Write method for the root object /// in the custom writer. /// {0}=Object type name /// {1}=Write method name generated by serializer for root object. /// </summary> const string TemplateWriteMethod = @" /// <summary>Writes an object of type {0}.</summary> internal void Write({0} obj) {{ {1}(obj); }}"; /// <summary> /// Template for custom serializer class members. /// {0}=Name of the serializer class /// {1}=Reader type name /// {2}=Writer type name /// {3}=Root object type /// </summary> const string TemplateCustomSerializer = @" {1} _reader; {2} _writer; /// <summary>Constructs the serializer with default reader and writer instances.</summary> public {0}() {{ }} /// <summary>Constructs the serializer with a pre-built reader.</summary> public {0}({1} reader) {{ _reader = reader; }} /// <summary>Constructs the serializer with a pre-built writer.</summary> public {0}({2} writer) {{ _writer = writer; }} /// <summary>Constructs the serializer with pre-built reader and writer.</summary> public {0}({1} reader, {2} writer) {{ _reader = reader; _writer = writer; }} /// <summary>See <see cref=""XmlSerializer.CreateReader""/>.</summary> protected override XmlSerializationReader CreateReader() {{ if (_reader != null) return _reader; else return new {1}(); }} /// <summary>See <see cref=""XmlSerializer.CreateWriter""/>.</summary> protected override XmlSerializationWriter CreateWriter() {{ if (_writer != null) return _writer; else return new {2}(); }} /// <summary>See <see cref=""XmlSerializer.Deserialize""/>.</summary> protected override object Deserialize(XmlSerializationReader reader) {{ if (!(reader is {1})) throw new ArgumentException(""reader""); return (({1})reader).Read(); }} /// <summary>See <see cref=""XmlSerializer.Serialize""/>.</summary> protected override void Serialize(object o, XmlSerializationWriter writer) {{ if (!(writer is {2})) throw new ArgumentException(""writer""); (({2})writer).Write(({3})o); }}"; #endregion Code templates #endregion Consts & Fields /// <summary> /// Generates the code for a type. /// </summary> /// <param name="type">The type to generate custom XmlSerializer for.</param> /// <param name="targetNamespace">Target namespace to use for the generated classes.</param> /// <returns>Raw C# source code.</returns> public static string GenerateCode(Type type, string targetNamespace) { #region Retrieve XmlSerializer output string output; XmlSerializer ser = new XmlSerializer(type); // Here starts the horrible reflection hacks! //ser.tempAssembly.assembly.Location FieldInfo fitempasm = ser.GetType().GetField("tempAssembly", BindingFlags.NonPublic | BindingFlags.Instance); object tempasm = fitempasm.GetValue(ser); FieldInfo fiasm = tempasm.GetType().GetField("assembly", BindingFlags.NonPublic | BindingFlags.Instance); Assembly asm = (Assembly) fiasm.GetValue(tempasm); string codebase = new Uri(asm.CodeBase).LocalPath; string code = Path.Combine( Path.GetDirectoryName(codebase), Path.GetFileNameWithoutExtension(codebase) + ".0.cs"); using (StreamReader sr = new StreamReader(code)) { output = sr.ReadToEnd(); } #endregion Retrieve XmlSerializer output #region Create CodeDom System.CodeDom.CodeNamespace ns = new System.CodeDom.CodeNamespace(targetNamespace); ns.Imports.Add(new CodeNamespaceImport("System.Xml.Serialization")); ns.Imports.Add(new CodeNamespaceImport("System")); CodeTypeDeclaration serializer = new CodeTypeDeclaration(type.Name + "Serializer"); serializer.BaseTypes.Add(typeof(XmlSerializer)); serializer.Comments.Add(new CodeCommentStatement( "<summary>Custom serializer for " + type.Name + " type.</summary", true)); ns.Types.Add(serializer); #endregion Create CodeDom #region Initial string manipulation & custom XmlSerializer creation // Remove assembly attribute. output = output.Replace("[assembly:System.Security.AllowPartiallyTrustedCallers()]", ""); // Replace namespace. output = output.Replace("Microsoft.Xml.Serialization.GeneratedAssembly", type.FullName + "Serialization"); // Give generated classes more friendly names. output = output.Replace("public class XmlSerializationWriter1", @"/// <remarks/> public class Writer"); output = output.Replace("public class XmlSerializationReader1", @"/// <remarks/> public class Reader"); // Find the method that is the entry point for reading the object. Match readmatch = PublicRead.Match(output); string rootobject = readmatch.Groups[PublicRootObject].Value; // Now add custom serializer members with the new info. string sm = String.Format( TemplateCustomSerializer, serializer.Name, rootobject + "Reader", rootobject + "Writer", type.FullName); serializer.Members.Add(new CodeSnippetTypeMember(sm)); #endregion Initial string manipulation & custom XmlSerializer creation #region Create custom event rising typed reader CodeTypeDeclaration reader = new CodeTypeDeclaration(rootobject + "Reader"); reader.BaseTypes.Add(serializer.Name + ".Reader"); ns.Types.Add(reader); // For each read method Match readm = PrivateMethods.Match(output); while (readm.Success) { string objectread = readm.Groups[ReadMethodObjectName].Value; // Skip the generic reading method. if (objectread.ToLower() == "object") { readm = readm.NextMatch(); continue; } // Create a delegate for the event to expose. CodeTypeDelegate del = new CodeTypeDelegate( objectread + "DeserializedHandler"); char[] name = objectread.ToCharArray(); name[0] = Char.ToLower(name[0]); del.Parameters.Add(new CodeParameterDeclarationExpression( readm.Groups[ReadMethodReturnType].Value, new string(name))); del.Comments.Add(new CodeCommentStatement("/// <remarks/>", true)); ns.Types.Add(del); // Expose event. CodeMemberEvent ev = new CodeMemberEvent(); ev.Name = objectread + "Deserialized"; ev.Attributes = MemberAttributes.Public; ev.Type = new CodeTypeReference(del.Name); ev.Comments.Add(new CodeCommentStatement("/// <remarks/>", true)); reader.Members.Add(ev); // Override base method. string mo = String.Format( TemplateMethodOverride, readm.Groups[ReadMethodReturnType].Value, readm.Groups[ReadMethodName].Value, del.Name, ev.Name, readm.Groups[ReadMethodArguments].Value, // Arguments contain type + parameter name. Remove type for method call. RemoveTypeFromArguments.Replace( readm.Groups[ReadMethodArguments].Value, "")); reader.Members.Add(new CodeSnippetTypeMember(mo)); readm = readm.NextMatch(); } // Add the "final" public read method. reader.Members.Add(new CodeSnippetTypeMember( String.Format(TemplateReadMethod, type.FullName, readmatch.Groups[PublicMethodName].Value))); // Turn original public method into internal protected output = PublicRead.Replace(output, "protected internal object ${method}"); // Turn all private methods into protected virtual, as they are overriden // by the custom event rising reader. output = PrivateMethods.Replace(output, @"/// <remarks/> protected virtual ${return} ${method} {"); #endregion Create custom event rising typed reader #region Create custom writer CodeTypeDeclaration writer = new CodeTypeDeclaration(rootobject + "Writer"); writer.BaseTypes.Add(serializer.Name + ".Writer"); ns.Types.Add(writer); // Find the method that is the entry point for writing the object. Match writematch = PublicWrite.Match(output); // Add the "final" public write method. writer.Members.Add(new CodeSnippetTypeMember( String.Format(TemplateWriteMethod, type.FullName, writematch.Groups[PublicMethodName].Value))); // Turn original public method into internal protected output = PublicWrite.Replace(output, "protected internal void ${method}"); #endregion Create custom writer #region Make generated reader and writer classes inner classes of custom serializer int indexofreader = output.IndexOf("public class Reader"); int indexofwriter = output.IndexOf("public class Writer"); string genw = output.Substring( indexofwriter, indexofreader - indexofwriter); string genr = output.Substring( indexofreader, output.Length - indexofreader - 4); // Add #pragma to avoid compiler warnings. // TODO: Check how to do this for v1.x. //genr = "#pragma warning disable CS0642\n" + genr + "\n#pragma warning restore CS0642"; //genw = "#pragma warning disable CS0642\n" + genw + "\n#pragma warning restore CS0642"; serializer.Members.Add(new CodeSnippetTypeMember(genr)); serializer.Members.Add(new CodeSnippetTypeMember(genw)); #endregion Make generated reader and writer classes inner classes of custom serializer #region Generate code from CodeDom ICodeGenerator gen = new Microsoft.CSharp.CSharpCodeProvider().CreateGenerator(); CodeGeneratorOptions opt = new CodeGeneratorOptions(); opt.BracingStyle = "C"; StringWriter finalcode = new StringWriter(); gen.GenerateCodeFromNamespace(ns, finalcode, opt); #endregion Generate code from CodeDom //output = finalcode.ToString() + output; return finalcode.ToString(); //return output; } } } |