Click here to Skip to main content
15,880,469 members
Articles / Programming Languages / C#

A tool for making C# decorators/proxies

Rate me:
Please Sign up or sign in to vote.
4.94/5 (27 votes)
6 Dec 2008CPOL4 min read 60.5K   485   82  
Describes a small VS add-in for making decorators from existing code.
using System;
using System.CodeDom;
using System.CodeDom.Compiler;
using System.Collections.Generic;
using System.IO;
using System.Text;
using System.Threading;
using System.Windows.Forms;
using System.Workflow.Runtime;
using DDW;
using DDW.Collections;
using EnvDTE;
using EnvDTE80;
using Microsoft.CSharp;
using Pfactor.Extras;
using Pfactor.Workflow;
using CodeNamespace = System.CodeDom.CodeNamespace;

namespace Pfactor
{
  public partial class DecoratorForm : Form
  {

    private readonly DTE2 app;
    private Project currentProject;
    /// <summary>
    /// Ridiculous.
    /// </summary>
    private bool suppressTreeEvents;

    private readonly Tooltip toolTip;


    public DecoratorForm(DTE2 app)
    {
      InitializeComponent();
      this.app = app;
      toolTip = new Tooltip();
    }




    public TreeNode FindNode(string text)
    {
      foreach (TreeNode tn in theTree.Nodes)
      {
        if (tn.Text == text)
          return tn;
        TreeNode tn2 = FindNode(tn, text);
        if (tn2 != null) return tn2;
      }
      return null;
    }


    private void AddFileToTree(string file)
    {
      if (!File.Exists(file))
        return;

      using (StreamReader sr = new StreamReader(file))
      {
        Lexer l = new Lexer(sr);
        TokenCollection tc = l.Lex();
        Parser p = new Parser(string.Empty);

        CompilationUnitNode cun = p.Parse(tc, l.StringLiterals);
        
        foreach (NamespaceNode nn in cun.Namespaces)
          AddNamespaceToTree(nn, null);
      }
    }

    private void AddNamespaceToTree(NamespaceNode nn, TreeNode parent)
    {
      if (nn == null) return;
      if (nn.Structs.Count + nn.Classes.Count == 0) return;
      // add the namespace itself
      TreeNode nnNode = FindNode(nn.Name.GenericIdentifier);
      if (nnNode == null)
      {
        if (parent == null)
          nnNode = theTree.Nodes.Add(nn.Name.GenericIdentifier);
        else
          nnNode = parent.Nodes.Add(nn.Name.GenericIdentifier);
      }

      // tag nnNode
      nnNode.Tag = nn;
      nnNode.ImageIndex = 0;

      // whoops!
      foreach (NamespaceNode nn2 in nn.Namespaces)
        AddNamespaceToTree(nn2, nnNode);

      foreach (ClassNode cn in nn.Classes)
      {
        TreeNode classNode = nnNode.Nodes.Add(cn.Name.Identifier);
        classNode.Tag = cn;
        classNode.ImageIndex = 1;
      }
      foreach (StructNode sn in nn.Structs)
      {
        TreeNode structNode = nnNode.Nodes.Add(sn.Name.Identifier);
        structNode.Tag = sn;
        structNode.ImageIndex = 1;
      }
    }

    private void btnOK_Click(object sender, EventArgs e)
    {
      MakeDecorators();
    }

    private void DecoratorForm_Load(object sender, EventArgs e)
    {
      LoadToolTips();

      if (app.SelectedItems.Count == 1)
      {
        SelectedItem si = app.SelectedItems.Item(1);
        currentProject = si.Project;
        IEnumerable<string> files = Util.GetProjectFiles(currentProject);

        // use existing files
        List<string> faultyFiles = new List<string>();
        foreach (string file in files)
        {
          try
          {
            AddFileToTree(file);
          } 
          catch
          {
            faultyFiles.Add(file);
          }
        }

        if (faultyFiles.Count > 0)
        {
          var sb = new StringBuilder();
          sb.AppendLine("The following files could not be parsed:");
          sb.AppendLine();
          foreach (string file in faultyFiles)
            sb.AppendLine(" - " + file);
          MessageBox.Show(
            sb.ToString(), "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning);
        }
      }
    }

    private void LoadToolTips()
    {
      toolTip.UseVisualStyle = toolTip.UseAnimation = toolTip.UseFading = true;
      toolTip.MaximumWidth = 320;
      toolTip.Opacity = 1.0f;
      toolTip.InitialDelay = 500;
      toolTip.FadeDelay = 250;
      toolTip.SetToolTip(btnOK.Handle, "Click to create the decorator for the chosen items and add it to the project.");
      toolTip.SetToolTip(tbClassName.Handle,
                         "Enter the name of the class to create here. A corresponding file with .cs extension will be created in the solution.");
    }

    private static void downRecursiveSetCheck(TreeNodeCollection tnc, bool state)
    {
      foreach (TreeNode tn in tnc)
      {
        tn.Checked = state;
        downRecursiveSetCheck(tn.Nodes, state);
      }
    }

    private static TreeNode FindNode(TreeNode parent, string text)
    {
      foreach (TreeNode tn in parent.Nodes)
        if (tn.Text == text)
          return tn;
      return null;
    }

    private static void GetClassesAndStructsFromNodes(ICollection<ClassNode> classes, TreeNodeCollection nodes)
    {
      if (nodes == null) return;
      foreach (TreeNode n in nodes)
      {
        if (n.Checked && n.Tag != null)
        {
          // StructNode derives from ClassNode
          ClassNode cn = n.Tag as ClassNode;
          if (cn != null) classes.Add(cn);
        }
        GetClassesAndStructsFromNodes(classes, n.Nodes);
      }
    }

    private void MakeDecorators()
    {
      // get all classes and structs that this is about to decorate
      var classes = new List<ClassNode>();
      GetClassesAndStructsFromNodes(classes, theTree.Nodes);
      // now build our new class
      CodeNamespace cn = new CodeNamespace(
        Util.GetProjectProperty(currentProject, "DefaultNamespace")
        );

      CodeTypeDeclaration decoratorClass = new CodeTypeDeclaration(tbClassName.Text)
      {
        IsClass = true
      };
      cn.Types.Add(decoratorClass);
      /* not a good idea
      foreach (ClassNode n in classes)
        decoratorClass.BaseTypes.Add("I" + n.Name.Identifier); */

      WorkflowRuntime wr = new WorkflowRuntime();
      AutoResetEvent are = new AutoResetEvent(false);
      wr.WorkflowCompleted += ((sender, e) => are.Set());
      wr.WorkflowTerminated += ((sender, e) => MessageBox.Show(e.Exception.Message));

      WorkflowInstance wi = wr.CreateWorkflow(
        typeof (BuildDecorator), new Dictionary<string, object>
          {
            {"Decorator", decoratorClass},
            {"Classes", classes}
          });
      wi.Start();
      are.WaitOne();

      // now the provider business
      var sb = new StringBuilder();
      StringWriter sw = new StringWriter(sb);
      CSharpCodeProvider provider = new CSharpCodeProvider();
      ICodeGenerator generator = provider.CreateGenerator(sw);
      generator.GenerateCodeFromNamespace(cn, sw, new CodeGeneratorOptions());
     
      // add this stuff to the solution
      Util.SaveDataAndAddToProject(
        app, 
        currentProject, 
        decoratorClass.Name + ".cs", 
        Util.RemoveEmptyLinesAndAt(sb.ToString()),
        true);
      Close();
    }

    private void theTree_AfterCheck(object sender, TreeViewEventArgs e)
    {
      if (!suppressTreeEvents)
      {
        downRecursiveSetCheck(e.Node.Nodes, e.Node.Checked);
        upRecursiveSetCheck(e.Node);
        updateOKButton();
      }
    }

    /// <summary>
    /// OK button is enabled only if at least one class/struct is
    /// selected and the class name is not empty.
    /// </summary>
    private void updateOKButton()
    { // hack
      var classes = new List<ClassNode>();
      GetClassesAndStructsFromNodes(classes, theTree.Nodes);
      btnOK.Enabled = classes.Count > 0 && tbClassName.Text.Length > 0;
    }

    private void upRecursiveSetCheck(TreeNode treeNode)
    {
      // if I have no parents, return
      if (treeNode.Parent == null) return;

      // are all my siblings checked
      bool allSet = true;
      foreach (TreeNode tn in treeNode.Parent.Nodes)
      {
        if (!tn.Checked) allSet = false;
      }

      // programmatically set parent
      if (allSet)
      {
        suppressTreeEvents = true;
        treeNode.Parent.Checked = true;
        suppressTreeEvents = false;
        upRecursiveSetCheck(treeNode.Parent);
      }
      else // some are unset
      {
        suppressTreeEvents = true;
        treeNode.Parent.Checked = false;
        suppressTreeEvents = false;
        upRecursiveSetCheck(treeNode.Parent);
      }
    }
  }
}

By viewing downloads associated with this article you agree to the Terms of Service and the article's licence.

If a file you wish to view isn't highlighted, and is a text file (not binary), please let us know and we'll add colourisation support for it.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Founder ActiveMesa
United Kingdom United Kingdom
I work primarily with the .NET technology stack, and specialize in accelerated code production via code generation (static or dynamic), aspect-oriented programming, MDA, domain-specific languages and anything else that gets products out the door faster. My languages of choice are C# and C++, though I'm open to suggestions.

Comments and Discussions