Linq provider : an attempt… part 5

Improving the toolbox : building our own expression tree visualizations

In the last two posts, I included sample visualizations of expression trees. I’ve built these using a visitor pattern, that produces HTML markup. This is a good example of the use of this pattern, so I’ll try to explain how it allows us to work with expression trees.

The expression tree visualization that I build is based on HTML / CSS3, using the code provided at http://thecodeplayer.com/walkthrough/css3-family-tree. Basically, the markup is formed of unordered lists (“ul / li” tags) that contains nested lists. You can view the generated HTML source by inspecting the DOM in the previous post.

The visualization builder is a class called TreeVisualizer, inheriting from ExpressionVisitor :

<p>internal class TreeVisualizer : ExpressionVisitor
{
    private StringBuilder builder;
    private HtmlTextWriter writer;
 
    private TreeVisualizer()
    {
        this.builder = new StringBuilder();
        this.writer = new HtmlTextWriter(
            new StringWriter(
                this.builder,
                CultureInfo.InvariantCulture),
            "    ");
    } [...]</p>

The constructor is private, the only way to use this class is through a static method called BuildVisualization :

internal static string BuildVisualization(Expression expression)
{
    TreeVisualizer visualizer = new TreeVisualizer();
    visualizer.Visit(expression);
    return visualizer.Visualization;
}

The returned property, Visualization, is defined this way :

public string Visualization
{
    get
    {
        return this.builder.ToString();
    }
}

The class uses also two utility methods :

  • GetSimplifiedType, which gives a user-friendly string representation of a given type.
private string GetSimplifiedType(Type type)
{
    if (!type.IsGenericType)
        return type.Name;
 
    string genericName = type.Name.Split('`').First();
 
    string genericArguments = string.Join(
        ", ",
        type.GenericTypeArguments.Select(
            t => GetSimplifiedType(t)));
 
    return string.Format(
        "{0}<{1}>",
        genericName,
        genericArguments);
}
  • VisitAndBuildTree, which adds content to the HtmlTextWriter based on the tree nodes that it visits :
private Expression VisitAndBuildTree(
    string nodeName,
    string nodeType,
    string nodeDescription,
    Func<Expression> childrenVisitorFunction = null)
{
    this.writer.RenderBeginTag("li");
    this.writer.WriteLine();
    this.writer.Indent++;
 
    this.writer.AddAttribute("href", "#");
    this.writer.RenderBeginTag("a");
 
    this.writer.AddAttribute("class", "node-name");
    this.writer.RenderBeginTag("span");
    this.writer.WriteEncodedText(nodeName);
    this.writer.RenderEndTag();
    this.writer.WriteBreak();
 
    if (!string.IsNullOrEmpty(nodeType))
    {
        this.writer.AddAttribute("class", "node-type");
        this.writer.RenderBeginTag("span");
        this.writer.WriteEncodedText(nodeType);
        this.writer.RenderEndTag();
        this.writer.WriteBreak();
    }
 
    this.writer.WriteEncodedText(nodeDescription);
    this.writer.RenderEndTag();
    this.writer.WriteLine();
 
    Expression baseReturn = null;
    if (childrenVisitorFunction != null)
    {
        this.writer.RenderBeginTag("ul");
        this.writer.WriteLine();
        this.writer.Indent++;
 
        baseReturn = childrenVisitorFunction();
 
        this.writer.Indent--;
        this.writer.RenderEndTag();
        this.writer.WriteLine();
 
    }
 
    this.writer.Indent--;
    this.writer.RenderEndTag();
    this.writer.WriteLine();
 
    return baseReturn;
}

With the two previous functions, we can now override each VisitSomeTypeOfNode method and insert the appropriate details in the HTML. For instance :

protected override Expression VisitBinary(
    BinaryExpression node)
{
    return VisitAndBuildTree(
        "Binary",
        string.Empty,
        node.NodeType.ToString(),
        () => base.VisitBinary(node));
}
protected override Expression VisitLambda<T>(
    Expression<T> node)
{
    return VisitAndBuildTree(
        "Lambda",
        GetSimplifiedType(node.Type),
        node.ToString(),
        () => base.VisitLambda<T>(node));
}
protected override Expression VisitMethodCall(
    MethodCallExpression node)
{
    return VisitAndBuildTree(
        "Call",
        GetSimplifiedType(node.Type),
        node.Method.Name,
        () => base.VisitMethodCall(node));
}

There are many methods, I’m not going to show them all here. I’ll just finish with a special one, VisitConstant. For this one, I just handled two special cases, when the constant is an IEnumerable, or when it is a string. This allows us to see the constants values for simple types in the tree, and a simplified type description if it is a generic IEnumerable.

protected override Expression VisitConstant(
    ConstantExpression node)
{
    string type = GetSimplifiedType(node.Type);
 
    string value;
    if (node.Type.IsGenericType
        && node.Type.FindInterfaces(
            (t, o) => t.Name.StartsWith("IEnumerable"),
            true).Any())
    {
        value = type;
    }
    else if(type == "String")
    {
        value = string.Concat(
            "\"",
            ((string)node.Value).Replace("\"", "\\\""),
            "\"");
    }
    else
    {
        value = node.Value.ToString();
    }
 
    VisitAndBuildTree(
        "Constant",
        type,
        value);
    return base.VisitConstant(node);
}

That’s all for tonight !

This entry was posted in LINQ and tagged , , , , , . Bookmark the permalink.

Comments are closed.