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 !

