| 1: | // -------------------------------------------------------------------------- | |
| 2: | // (c) Copyright Bill Dawson 2002 | |
| 3: | // http://www.BillDawson.com | |
| 4: | // | |
| 5: | // You are free to use this source code in your projects. Please leave | |
| 6: | // this copyright message intact. | |
| 7: | // -------------------------------------------------------------------------- | |
| 8: | ||
| 9: | namespace com.billdawson.dotnet.utils | |
| 10: | { | |
| 11: | using System; | |
| 12: | using System.Reflection; | |
| 13: | using Microsoft.CSharp; | |
| 14: | using Microsoft.VisualBasic; | |
| 15: | using Microsoft.JScript; | |
| 16: | using System.CodeDom; | |
| 17: | using System.CodeDom.Compiler; | |
| 18: | using System.Collections; | |
| 19: | using System.IO; | |
| 20: | ||
| 21: | /// <summary> | |
| 22: | /// Static methods to overcome limitations of public fields by turning them | |
| 23: | /// into public properties of a new wrapper class that redirects calls to | |
| 24: | /// the orig class fields. | |
| 25: | /// </summary> | |
| 26: | /// <remarks>For example, you may be using a class that you cannot | |
| 27: | /// or do not wish to change, but that has one or more public fields | |
| 28: | /// that you would rather access as public properties. You might | |
| 29: | /// want this so that you can bind the type directly as a data item | |
| 30: | /// in a DataGrid. This class's Wrap methods can be used to create | |
| 31: | /// a second class that wraps the first and uses public properties to | |
| 32: | /// access the original class's public fields. | |
| 33: | /// </remarks> | |
| 34: | public class Field2PropWrap | |
| 35: | { | |
| 36: | /// <summary> | |
| 37: | /// Code providers for these languages can be instantiated by | |
| 38: | /// this library. | |
| 39: | /// </summary> | |
| 40: | /// <remarks>However, if you have a different code provider | |
| 41: | /// you can also use the Transform method that accepts a provider | |
| 42: | /// directly as a parameter. | |
| 43: | /// </remarks> | |
| 44: | public enum TransformLanguage{ | |
| 45: | CSharp, | |
| 46: | VB, | |
| 47: | JScript | |
| 48: | } | |
| 49: | ||
| 50: | /// <summary> | |
| 51: | /// Creates a new type based on the passed type. This new type will | |
| 52: | /// have public properties named the same as the original type's | |
| 53: | /// public fields. Code inside the new type will redirect property calls | |
| 54: | /// to the orig type's fields. New code is written to stream. | |
| 55: | /// </summary> | |
| 56: | /// <param name="origtype">The original type that will be wrapped.</param> | |
| 57: | /// <param name="newNamespace">Namespace string for the new type.</param> | |
| 58: | /// <param name="yourLanguage">Any CodeDomProvider. This allows you to use languages other than the three recognized internally.</param> | |
| 59: | /// <param name="writer">A text writer to an output stream to which the new code will be sent.</param> | |
| 60: | public static void Wrap(Type origtype, string newNamespace, CodeDomProvider yourLanguage, TextWriter writer) | |
| 61: | { | |
| 62: | string origTypeFullName = origtype.FullName; | |
| 63: | string origTypeShortName = origtype.Name; | |
| 64: | ||
| 65: | ArrayList fields = new System.Collections.ArrayList(); | |
| 66: | ||
| 67: | // find all public fields; these are the things we want | |
| 68: | // to turn into public properties in our new type. | |
| 69: | foreach(FieldInfo fi in origtype.GetFields( )) | |
| 70: | { | |
| 71: | if (fi.IsPublic) | |
| 72: | fields.Add(fi); | |
| 73: | } | |
| 74: | ||
| 75: | ||
| 76: | if (fields.Count==0) // there aren't any; nothing to do | |
| 77: | throw new TypeHasNoPublicFieldsException(origTypeFullName); | |
| 78: | ||
| 79: | // transform ArrayList to typed array | |
| 80: | FieldInfo[] fis = null; | |
| 81: | fis = (FieldInfo[])fields.ToArray(typeof(FieldInfo)); | |
| 82: | ||
| 83: | // The System.CodeDom stuff we need to generate code | |
| 84: | CodeNamespace ns = new CodeNamespace(newNamespace); | |
| 85: | ||
| 86: | // new type has same name as old but will be put | |
| 87: | // in the new namespace | |
| 88: | CodeTypeDeclaration newtype = | |
| 89: | new CodeTypeDeclaration( origTypeShortName ); | |
| 90: | ||
| 91: | // private var for new class, to hold instance | |
| 92: | // of old class | |
| 93: | CodeMemberField pvar = | |
| 94: | new CodeMemberField(origTypeFullName, "_wrapped"); | |
| 95: | pvar.Attributes= MemberAttributes.Private ; | |
| 96: | newtype.Members.Add(pvar); | |
| 97: | ||
| 98: | // create a reference expression to reference that | |
| 99: | // _wrapped variable later in order to assign to it. | |
| 100: | CodeVariableReferenceExpression pvarRef = | |
| 101: | new CodeVariableReferenceExpression(pvar.Name); | |
| 102: | ||
| 103: | ||
| 104: | // Constructor that accepts the old type | |
| 105: | // and sets private var to the old type | |
| 106: | CodeParameterDeclarationExpression param = | |
| 107: | new CodeParameterDeclarationExpression(origtype,"orig"); | |
| 108: | CodeConstructor constructor = new CodeConstructor(); | |
| 109: | constructor.Parameters.Add(param); | |
| 110: | constructor.Attributes = MemberAttributes.Public; | |
| 111: | ||
| 112: | // Create a reference to that parameter as a variable so we can | |
| 113: | // use it in an assignment statement | |
| 114: | CodeVariableReferenceExpression paramRef = | |
| 115: | new CodeVariableReferenceExpression(param.Name); | |
| 116: | ||
| 117: | // Make the assignment statement that is inside the constructor | |
| 118: | // i.e., in C# , _wrapped = orig; | |
| 119: | constructor.Statements.Add(new CodeAssignStatement(pvarRef,paramRef)); | |
| 120: | ||
| 121: | // Add the constructor as a member to the new type | |
| 122: | newtype.Members.Add(constructor); | |
| 123: | ||
| 124: | // add our new type to the new namespace. | |
| 125: | ns.Types.Add(newtype); | |
| 126: | ||
| 127: | // add public properties where old class has fields | |
| 128: | foreach(FieldInfo fi in fis) | |
| 129: | { | |
| 130: | // for each pulbic field in the orig, make a public property | |
| 131: | // in thenew | |
| 132: | CodeMemberProperty prop = new CodeMemberProperty(); | |
| 133: | // give new property same name and type as old field. | |
| 134: | prop.Name = fi.Name; | |
| 135: | prop.Type = new CodeTypeReference( fi.FieldType ); | |
| 136: | prop.Attributes = MemberAttributes.Public; | |
| 137: | ||
| 138: | // reference to orig class variable is pvarref from above | |
| 139: | // (referencing private variable we named "_wrapped") | |
| 140: | // Here we reference its public field that has the | |
| 141: | // same name as the public property we are creating | |
| 142: | CodeFieldReferenceExpression refToOldField = | |
| 143: | new CodeFieldReferenceExpression(pvarRef, fi.Name); | |
| 144: | ||
| 145: | // a return expression, simply returns the value of | |
| 146: | // the base field, for the Get statement | |
| 147: | CodeMethodReturnStatement ret = | |
| 148: | new CodeMethodReturnStatement(refToOldField); | |
| 149: | ||
| 150: | // the property get accessor | |
| 151: | prop.GetStatements.Add(ret); | |
| 152: | ||
| 153: | // an assignment expression for the set accessor | |
| 154: | prop.SetStatements.Add( | |
| 155: | new CodeAssignStatement( | |
| 156: | refToOldField,new CodePropertySetValueReferenceExpression())); | |
| 157: | ||
| 158: | // add this property member to the new type | |
| 159: | newtype.Members.Add(prop); | |
| 160: | } | |
| 161: | ||
| 162: | // generate the code | |
| 163: | generateNamespaceToStream(ns, writer, yourLanguage); | |
| 164: | ||
| 165: | } | |
| 166: | ||
| 167: | /// <summary> | |
| 168: | /// Creates a new type based on the passed type. This new type will | |
| 169: | /// have public properties named the same as the original type's | |
| 170: | /// public fields. Code inside the new type will redirect property calls | |
| 171: | /// to the orig type's fields. Returns the new code as a string. | |
| 172: | /// </summary> | |
| 173: | /// <param name="origtype">The original type that will be wrapped.</param> | |
| 174: | /// <param name="newNamespace">Namespace string for the new type.</param> | |
| 175: | /// <param name="yourLanguage">Any CodeDomProvider. This allows you to use languages other than the three recognized internally.</param> | |
| 176: | /// <returns>String, the generated code for the new class.</returns> | |
| 177: | public static string Wrap(Type origtype, string newNamespace, CodeDomProvider yourLanguage) | |
| 178: | { | |
| 179: | MemoryStream ms = new MemoryStream(); | |
| 180: | TextWriter tw = new StreamWriter(ms); | |
| 181: | Wrap(origtype, newNamespace, yourLanguage, tw); | |
| 182: | ms.Position=0; | |
| 183: | StreamReader sr = new StreamReader(ms); | |
| 184: | string result = sr.ReadToEnd(); | |
| 185: | sr.Close(); | |
| 186: | ms.Close(); | |
| 187: | return result; | |
| 188: | } | |
| 189: | ||
| 190: | /// <summary> | |
| 191: | /// Creates a new type based on the passed type. This new type will | |
| 192: | /// have public properties named the same as the original type's | |
| 193: | /// public fields. Code inside the new type will redirect property calls | |
| 194: | /// to the orig type's fields. Returns the new code as a string. | |
| 195: | /// </summary> | |
| 196: | /// <param name="origtype">The original type that will be wrapped.</param> | |
| 197: | /// <param name="newNamespace">Namespace string for the new type.</param> | |
| 198: | /// <param name="language">The supported language enum member.</param> | |
| 199: | /// <returns>String, the generated code for the new class.</returns> | |
| 200: | public static string Transform(Type origtype, string newNamespace, TransformLanguage language) | |
| 201: | { | |
| 202: | CodeDomProvider provider = getProviderFromEnum(language); | |
| 203: | return Wrap(origtype, newNamespace, provider); | |
| 204: | } | |
| 205: | ||
| 206: | /// <summary> | |
| 207: | /// Creates a new type based on the passed type. This new type will | |
| 208: | /// have public properties named the same as the original type's | |
| 209: | /// public fields. Code inside the new type will redirect property calls | |
| 210: | /// to the orig type's fields. Writes the new code to a stream. | |
| 211: | /// </summary> | |
| 212: | /// <param name="origtype">The original type that will be wrapped.</param> | |
| 213: | /// <param name="newNamespace">Namespace string for the new type.</param> | |
| 214: | /// <param name="language">The supported language enum member.</param> | |
| 215: | /// <param name="writer">A TextWriter to an output stream to which the new code will be sent.</param> | |
| 216: | public static void Wrap(Type origtype, string newNamespace, TransformLanguage language, TextWriter writer) | |
| 217: | { | |
| 218: | CodeDomProvider provider = getProviderFromEnum(language); | |
| 219: | Wrap(origtype, newNamespace, provider, writer); | |
| 220: | } | |
| 221: | ||
| 222: | private static CodeDomProvider getProviderFromEnum(TransformLanguage tl) | |
| 223: | { | |
| 224: | CodeDomProvider provider=null; | |
| 225: | if (tl==TransformLanguage.VB) | |
| 226: | provider = new VBCodeProvider(); | |
| 227: | else if (tl==TransformLanguage.CSharp) | |
| 228: | provider = new CSharpCodeProvider(); | |
| 229: | else if (tl==TransformLanguage.JScript) | |
| 230: | provider = new JScriptCodeProvider(); | |
| 231: | else | |
| 232: | throw new UnknownLanguageException("unk"); | |
| 233: | return provider; | |
| 234: | } | |
| 235: | ||
| 236: | private static void generateNamespaceToStream(CodeNamespace ns, Stream stream, CodeDomProvider provider) | |
| 237: | { | |
| 238: | generateNamespaceToStream(ns, new StreamWriter(stream), provider); | |
| 239: | } | |
| 240: | ||
| 241: | private static void generateNamespaceToStream(CodeNamespace ns, TextWriter tw, CodeDomProvider provider) | |
| 242: | { | |
| 243: | CodeCompileUnit compileUnit = new CodeCompileUnit(); | |
| 244: | compileUnit.Namespaces.Add(ns); | |
| 245: | ICodeGenerator generator = provider.CreateGenerator(); | |
| 246: | CodeGeneratorOptions options = new CodeGeneratorOptions(); | |
| 247: | options.BracingStyle = "C"; | |
| 248: | generator.GenerateCodeFromCompileUnit(compileUnit, tw, options); | |
| 249: | tw.Flush(); | |
| 250: | } | |
| 251: | ||
| 252: | } | |
| 253: | } |
This page was automatically generated by SharpDevelop.