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 :

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), " "); } [...]

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.