Wednesday, August 15, 2007

Sharepoint Class Wrapper

Many sharepoint classes are not available to inherit from because the default contructor is hidden. The following (quick and untested!) code uses the CodeDom to generate a wrapper class.

using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Reflection;
using Microsoft.SharePoint;
 
namespace DevHoleDemo.ClassWrapper
{
    class Program
    {
        static void Main(string[] args)
        {
            ClassWrapper w = new ClassWrapper("DevHoleDemo", "SPWebWrapper", typeof(SPWeb));
            string s = w.GetCode();
        }
    }
 
    public class ClassWrapper
    {
        string m_namespace;
        string m_className;
        SortedDictionary<string, int> m_imports;
        SortedDictionary<string, int> m_modules;
        StringBuilder m_sb;
        Type m_t;
 
        public ClassWrapper(string targetNamespace, string targetClassName, object source)
            : this(targetNamespace, targetClassName, source.GetType())
        {
        }
 
        public ClassWrapper(string targetNamespace, string targetClassName, Type t)
        {
            m_namespace = targetNamespace;
            m_className = targetClassName;
            m_t = t;
            m_imports = new SortedDictionary<string, int>();
            m_modules = new SortedDictionary<string, int>();
        }
 
        public string GetCode()
        {
            return GetCode("CS");
        }
 
        public string GetCode(string language)
        {
            Generate(language);
            string results = m_sb.ToString();
            results = results.Replace("void implicit_operator_", "implicit operator ");
            return results;
        }
 
        protected void AddNamespace(Type t)
        {
            if (m_imports.ContainsKey(t.Namespace))
                m_imports[t.Namespace]++;
            else
                m_imports.Add(t.Namespace, 1);
            if (m_modules.ContainsKey(t.Module.Name))
                m_modules[t.Module.Name]++;
            else
                m_modules.Add(t.Module.Name, 1);
        }
 
        protected CodeTypeReference GetCodeTypeReference(Type t)
        {
            AddNamespace(t);
            if (t.IsByRef)
                return new CodeTypeReference(t.Name.TrimEnd('&'));
            else
                return new CodeTypeReference(t);
        }
 
        protected void Generate(string language)
        {
 
            m_sb = new StringBuilder();
            TextWriter tw = new StringWriter(m_sb);
            CodeDomProvider cdp = CodeDomProvider.CreateProvider(language);
            CodeTypeReferenceExpression tre = new CodeTypeReferenceExpression(m_t);
 
            //namespace
            CodeNamespace ns = new CodeNamespace(m_namespace);
 
            //class
            CodeTypeDeclaration class1 = new CodeTypeDeclaration(m_className);
            ns.Types.Add(class1);
            class1.IsClass = true;
 
            //base object field
            CodeMemberField cmf1 = new CodeMemberField(GetCodeTypeReference(m_t), "m_" + m_t.Name);
            class1.Members.Add(cmf1);
 
            //constructor
            CodeConstructor cc = new CodeConstructor();
            cc.Attributes = MemberAttributes.Public;
            CodeParameterDeclarationExpression cpde1 = new CodeParameterDeclarationExpression(GetCodeTypeReference(m_t), m_t.Name);
            cc.Parameters.Add(cpde1);
            CodeFieldReferenceExpression cfre1 = new CodeFieldReferenceExpression(new CodeThisReferenceExpression(), "m_" + m_t.Name);
            cc.Statements.Add(new CodeAssignStatement(cfre1, new CodeArgumentReferenceExpression(m_t.Name)));
            class1.Members.Add(cc);
 
            //methods
            foreach (MethodInfo mi in m_t.GetMethods(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy))
            {
                if (!mi.IsSpecialName)
                {
                    CodeMemberMethod meth = new CodeMemberMethod();
                    meth.Attributes &= ~MemberAttributes.AccessMask & ~MemberAttributes.ScopeMask;
                    meth.Attributes |= MemberAttributes.Public;
                    if ((mi.Attributes & MethodAttributes.Static) == MethodAttributes.Static)
                        meth.Attributes |= MemberAttributes.Static;
                    if ((mi.Attributes & MethodAttributes.Virtual) != MethodAttributes.Virtual)
                        meth.Attributes |= MemberAttributes.Final;
                    meth.Name = mi.Name;
                    List<CodeExpression> prms = new List<CodeExpression>();
                    foreach (ParameterInfo ri in mi.GetParameters())
                    {
                        CodeParameterDeclarationExpression cpde2 = new CodeParameterDeclarationExpression(GetCodeTypeReference(ri.ParameterType), ri.Name);
                        CodeArgumentReferenceExpression care1 = new CodeArgumentReferenceExpression(ri.Name);
                        CodeDirectionExpression cde = null;
                        if (ri.IsOut)
                        {
                            cpde2.Direction = FieldDirection.Out;
                            cde = new CodeDirectionExpression(FieldDirection.Out, care1);
                        }
                        meth.Parameters.Add(cpde2);
                        if (cde == null)
                            prms.Add(care1);
                        else
                            prms.Add(cde);
                    }
 
                    CodeMethodReferenceExpression cmre1;
                    if ((mi.Attributes & MethodAttributes.Static) == MethodAttributes.Static)
                        cmre1 = new CodeMethodReferenceExpression(tre, mi.Name);
                    else
                        cmre1 = new CodeMethodReferenceExpression(cfre1, mi.Name);
 
                    if (mi.ReturnType == typeof(void))
                    {
                        meth.Statements.Add(new CodeMethodInvokeExpression(cmre1, prms.ToArray()));
                    }
                    else
                    {
                        meth.Statements.Add(new CodeMethodReturnStatement(new CodeMethodInvokeExpression(cmre1, prms.ToArray())));
                    }
                    meth.ReturnType = GetCodeTypeReference(mi.ReturnType);
 
                    class1.Members.Add(meth);
                }
            }
 
            //properties
            foreach (PropertyInfo pi in m_t.GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.Static | BindingFlags.FlattenHierarchy))
            {
                CodeMemberProperty prop = new CodeMemberProperty();
                prop.Name = pi.Name;
                prop.Attributes &= ~MemberAttributes.AccessMask & ~MemberAttributes.ScopeMask;
                prop.Attributes |= MemberAttributes.Public | MemberAttributes.Final;
                List<CodeExpression> indices = new List<CodeExpression>();
                if (pi.GetIndexParameters().Length > 0)
                {
                    foreach (ParameterInfo pii in pi.GetIndexParameters())
                    {
                        CodeParameterDeclarationExpression cpde3 = new CodeParameterDeclarationExpression(GetCodeTypeReference(pii.ParameterType), pii.Name);
                        prop.Parameters.Add(cpde3);
                        CodeArgumentReferenceExpression care3 = new CodeArgumentReferenceExpression(pii.Name);
                        indices.Add(care3);
                    }
 
                }
                prop.Type = GetCodeTypeReference(pi.PropertyType);
                prop.HasGet = pi.GetGetMethod() != null;
                if (prop.HasGet)
                {
                    if (pi.GetIndexParameters().Length > 0)
                        prop.GetStatements.Add(new CodeMethodReturnStatement(new CodeArrayIndexerExpression(cfre1, indices.ToArray())));
                    else
                        prop.GetStatements.Add(new CodeMethodReturnStatement(new CodeFieldReferenceExpression(cfre1, pi.Name)));
 
                }
                prop.HasSet = pi.GetSetMethod() != null;
                if (prop.HasSet)
                {
                    if (pi.GetIndexParameters().Length > 0)
                        prop.SetStatements.Add(new CodeAssignStatement(new CodeArrayIndexerExpression(cfre1, indices.ToArray()), new CodePropertySetValueReferenceExpression()));
                    else
                        prop.SetStatements.Add(new CodeAssignStatement(new CodeFieldReferenceExpression(cfre1, pi.Name), new CodePropertySetValueReferenceExpression()));
                }
                class1.Members.Add(prop);
            }
 
            //implicit casts
            CodeMemberMethod castFrom = new CodeMemberMethod();
            castFrom.Name = "implicit_operator_" + m_t.Name;
            castFrom.Attributes &= ~MemberAttributes.AccessMask & ~MemberAttributes.ScopeMask;
            castFrom.Attributes |= MemberAttributes.Public | MemberAttributes.Final | MemberAttributes.Static;
            CodeParameterDeclarationExpression cpde4 = new CodeParameterDeclarationExpression(new CodeTypeReference(m_className), m_className);
            castFrom.Parameters.Add(cpde4);
            CodeArgumentReferenceExpression care4 = new CodeArgumentReferenceExpression(m_className);
            CodeFieldReferenceExpression cfre3 = new CodeFieldReferenceExpression(care4, "m_" + m_t.Name);
            castFrom.Statements.Add(new CodeMethodReturnStatement(cfre3));
            castFrom.ReturnType = new CodeTypeReference(typeof(void));
            class1.Members.Add(castFrom);
 
            CodeMemberMethod castTo = new CodeMemberMethod();
            castTo.Name = "implicit_operator_" + m_className;
            castTo.Attributes &= ~MemberAttributes.AccessMask & ~MemberAttributes.ScopeMask;
            castTo.Attributes |= MemberAttributes.Public | MemberAttributes.Final | MemberAttributes.Static;
            CodeParameterDeclarationExpression cpde5 = new CodeParameterDeclarationExpression(new CodeTypeReference(m_t), m_t.Name);
            castTo.Parameters.Add(cpde5);
            CodeArgumentReferenceExpression care5 = new CodeArgumentReferenceExpression(m_t.Name);
            CodeObjectCreateExpression coce = new CodeObjectCreateExpression(new CodeTypeReference(m_className), care5);
            castTo.Statements.Add(new CodeMethodReturnStatement(coce));
            castTo.ReturnType = new CodeTypeReference(typeof(void));
            class1.Members.Add(castTo);
 
            //write imports
            foreach (KeyValuePair<string, int> kvp in m_imports)
            {
                ns.Imports.Add(new CodeNamespaceImport(kvp.Key));
            }
 
            //write modules comments
            ns.Comments.Add(new CodeCommentStatement("References to the following modules must be added:"));
            foreach (KeyValuePair<string, int> kvp in m_modules)
            {
                ns.Comments.Add(new CodeCommentStatement(string.Format("\t {0}", kvp.Key)));
            }
 
            cdp.GenerateCodeFromNamespace(ns, tw, null);
            tw.Close();
        }
    }
}