понедельник, 25 января 2010 г.

 

Deep pretty-printing in Java

Sometimes I need to insert debug logging into the code to breifly look into input or output parameters of some service (says EJB). If object, I want to look, has a big aggregation hierarchy It may take a long way to start debugger and manually expand all the leafs of a tree that represents this object. The good solution here is to make some aspest (Interceptor in EJB terms) that print input and output of a service.

Apache has a library for pretty-printing objects inside commons-lang. So the easiest way is just to write out :



import org.apache.commons.lang.builder.ReflectionToStringBuilder;
//...
String s = ReflectionToStringBuilder.toString(param);
System.out.println(s);


It's very quick and helpful when you need to print java-bean with lots of primitive field. But if hierarchy has more then one level, we need to dig deeper. I found quite a good solution based on inheriring org.apache.commons.lang.builder.ToStringStyle.
So that is it:


public class MyToStringStyle extends ToStringStyle {
int offset;

public MyToStringStyle () {
this(0);
}

private MyToStringStyle (int offset) {
this.offset = offset;
String off = "";
for (int i=0; i < offset; i++) off += " ";
this.setContentStart("[");
this.setFieldSeparator(SystemUtils.LINE_SEPARATOR + off + " ");
this.setFieldSeparatorAtStart(true);
this.setContentEnd(SystemUtils.LINE_SEPARATOR + off + "]");
}

protected void appendDetail(StringBuffer buffer, String fieldName, Collection col) {
buffer.append('[');
for (Object o: col) {
buffer.append(ReflectionToStringBuilder.toString(o, new MyToStringStyle(offset + 1)));
buffer.append(',');
}
if (buffer.charAt(buffer.length()-1) == ',') buffer.setCharAt(buffer.length()-1, ']');
}

protected void appendDetail(StringBuffer buffer, String fieldName, Object value) {
if (value.getClass().getName().startsWith("com.my.package")) {
buffer.append(ReflectionToStringBuilder.toString(value, new MyToStringStyle(offset + 1)));
} else {
super.appendDetail(buffer, fieldName, value);
}

}
}


As you can see here I've got a filter for going deeper - we only do it if class belong to our package com.my.package, but you can replace the filter's logic by your need. Another consideration is that this code will work for tree-like and DAG-like hierarchies but not for graphes with cycles. You can improve algorythm by introdusing HashSet of all visited graph nodes. So, when you meet node second time, just do nothing (or print backreference to it).

Oh, almost forgot! This is how to use a new class:


String s = ReflectionToStringBuilder.toString(value, new MyToStringStyle()));
System.out.println(s);

Ярлыки:


Комментарии:

Отправить комментарий

Подпишитесь на каналы Комментарии к сообщению [Atom]





<< Главная страница

This page is powered by Blogger. Isn't yours?

Подпишитесь на каналы Сообщения [Atom]