package com.google.code.joto.ast.valueholder.util; import java.io.PrintStream; import java.lang.reflect.Field; import java.util.Collection; import java.util.IdentityHashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import com.google.code.joto.ast.valueholder.ValueHolderAST.AbstractObjectValueHolder; import com.google.code.joto.ast.valueholder.ValueHolderAST.ArrayEltRefValueHolder; import com.google.code.joto.ast.valueholder.ValueHolderAST.CollectionEltRefValueHolder; import com.google.code.joto.ast.valueholder.ValueHolderAST.CollectionValueHolder; import com.google.code.joto.ast.valueholder.ValueHolderAST.FieldValueHolder; import com.google.code.joto.ast.valueholder.ValueHolderAST.ImmutableObjectValueHolder; import com.google.code.joto.ast.valueholder.ValueHolderAST.MapEntryKeyRefValueHolder; import com.google.code.joto.ast.valueholder.ValueHolderAST.MapEntryValueHolder; import com.google.code.joto.ast.valueholder.ValueHolderAST.MapEntryValueRefValueHolder; import com.google.code.joto.ast.valueholder.ValueHolderAST.MapValueHolder; import com.google.code.joto.ast.valueholder.ValueHolderAST.ObjectValueHolder; import com.google.code.joto.ast.valueholder.ValueHolderAST.PrimitiveArrayEltValueHolder; import com.google.code.joto.ast.valueholder.ValueHolderAST.PrimitiveArrayValueHolder; import com.google.code.joto.ast.valueholder.ValueHolderAST.PrimitiveFieldValueHolder; import com.google.code.joto.ast.valueholder.ValueHolderAST.RefArrayValueHolder; import com.google.code.joto.ast.valueholder.ValueHolderAST.RefFieldValueHolder; import com.google.code.joto.ast.valueholder.ValueHolderAST.RefObjectValueHolder; import com.google.code.joto.ast.valueholder.ValueHolderVisitor; /** * PrettyPrinter for AbstractObjectValueHolder to String representation. * <p/> * Implemented as a Visitor design pattern on AbstractObjectValueHolder class hierarchy */ public class ValueHolderPrettyPrinter implements ValueHolderVisitor { private PrintStream out; private boolean printLinksFrom; private int indent; private int refIdGenerator = 1; private Map<AbstractObjectValueHolder,Integer> obj2id = new IdentityHashMap<AbstractObjectValueHolder,Integer>(); private Map<AbstractObjectValueHolder,Boolean> alreadySeenRefs = new IdentityHashMap<AbstractObjectValueHolder,Boolean>(); // ------------------------------------------------------------------------- public ValueHolderPrettyPrinter(PrintStream out) { this.out = out; } // ------------------------------------------------------------------------- public boolean isPrintLinksFrom() { return printLinksFrom; } public void setPrintLinksFrom(boolean printLinksFrom) { this.printLinksFrom = printLinksFrom; } // ------------------------------------------------------------------------- public int getObjId(AbstractObjectValueHolder p) { Integer refId = obj2id.get(p); if (refId == null) { int id = refIdGenerator++; refId = new Integer(id); obj2id.put(p, refId); } return refId.intValue(); } public void visitOrPrintRef(AbstractObjectValueHolder p) { if (p == null) { return; } Boolean marked = alreadySeenRefs.get(p); Integer refId = getObjId(p); if (marked != null) { println(" object refId=" + refId); // no recurse! } else { alreadySeenRefs.put(p, Boolean.TRUE); println(" newobject id=" + refId); incrIndent(); // recurse p.visit(this); decrIndent(); } } protected void printObjLinksFrom(AbstractObjectValueHolder p) { List<RefObjectValueHolder> linksFrom = p.getLinksFrom(); indentPrint("linksFrom " + linksFrom.size() + " elt"); if (linksFrom != null && !linksFrom.isEmpty()) { if (linksFrom.size() > 1) { print("s"); } print(": "); for (Iterator<RefObjectValueHolder> iter = linksFrom.iterator(); iter.hasNext();) { RefObjectValueHolder link = iter.next(); AbstractObjectValueHolder from = link.getFrom(); int fromId = getObjId(from); print(Integer.toString(fromId)); if (iter.hasNext()) { print(", "); } } } println(); } // ------------------------------------------------------------------------- @Override public void caseObject(ObjectValueHolder p) { indentPrintln("Object class:" + p.getObjClass().getName()); incrIndent(); printObjLinksFrom(p); for(Map.Entry<Field,FieldValueHolder> entry : p.getFieldsValuesMap().entrySet()) { indentPrint("field: " + entry.getKey().getType().getName() + " " + entry.getKey().getName()); FieldValueHolder fvh = entry.getValue(); fvh.visit(this); } decrIndent(); } @Override public void casePrimitiveField(PrimitiveFieldValueHolder node) { Object value = node.getValue(); print(" = " + value); println(); } @Override public void caseRefField(RefFieldValueHolder node) { AbstractObjectValueHolder refVH = node.getTo(); if (refVH == null) { println(" = Null"); } else if (refVH != null) { visitOrPrintRef(refVH); } } @Override public void casePrimitiveArray(PrimitiveArrayValueHolder<?> p) { PrimitiveArrayEltValueHolder<?>[] array = p.getHolderArray(); int len = array.length; print(p.getObjClass().getComponentType().getName() + "[" + len + "] = { "); for (int i = 0; i < len; i++) { PrimitiveArrayEltValueHolder<?> elt = array[i]; elt.visit(this); if (i + 1 < len) { print(", "); } } println(" }"); } @Override public void casePrimitiveArrayElt(PrimitiveArrayEltValueHolder<?> p) { Object valueElt = p.getValue(); if (valueElt != null) { print(valueElt.toString()); } else { print("ERROR null primitive value"); } } @Override public void caseRefArray(RefArrayValueHolder p) { AbstractObjectValueHolder[] array = p.getElts(); int len = array.length; indentPrintln("object array " + p.getObjClass().getComponentType().getName() + "[" + len + "] = { "); for (int i = 0; i < len; i++) { AbstractObjectValueHolder elt = array[i]; indentPrint("elt[" + i + "/" + len + ": "); elt.visit(this); } println(" }"); } @Override public void caseRefArrayElt(ArrayEltRefValueHolder p) { if (p.getTo() != null) { visitOrPrintRef(p.getTo()); } else { print("Null"); } } @Override public void caseImmutableObjectValue(ImmutableObjectValueHolder p) { Object value = p.getValue(); indentPrintln("" + value.getClass().getName() + " " + value); } @Override public void caseCollection(CollectionValueHolder p) { Collection<CollectionEltRefValueHolder> elts = p.getEltRefs(); int len = elts.size(); indentPrintln("collection (" + p.getObjClass() + ") " + len + " elt(s)"); incrIndent(); int index = 0; for(CollectionEltRefValueHolder elt : elts) { indentPrint("elt[" + index + "/" + len + "]: "); elt.visit(this); index++; } decrIndent(); } @Override public void caseCollectionElt(CollectionEltRefValueHolder p) { visitOrPrintRef(p.getTo()); } @Override public void caseMap(MapValueHolder p) { Collection<MapEntryValueHolder> entries = p.getEntries(); int len = entries.size(); indentPrintln("map (" + p.getObjClass() + ") " + len + " elt(s)"); incrIndent(); int index = 0; for(MapEntryValueHolder entry : entries) { indentPrintln("entry[" + index + "/" + len + "]:"); entry.visit(this); index++; } decrIndent(); } @Override public void caseMapEntry(MapEntryValueHolder p) { // incrIndent(); // indentPrintln("mapEntry"); incrIndent(); p.getKey().visit(this); decrIndent(); incrIndent(); p.getValue().visit(this); decrIndent(); // decrIndent(); } @Override public void caseMapEntryKey(MapEntryKeyRefValueHolder p) { indentPrint("mapEntry key "); visitOrPrintRef(p.getTo()); } @Override public void caseMapEntryValue(MapEntryValueRefValueHolder p) { indentPrint("mapEntry value "); visitOrPrintRef(p.getTo()); } // ------------------------------------------------------------------------- public void incrIndent() { indent++; } public void decrIndent() { indent--; } public void printIndent() { for (int i = 0; i < indent; i++) { out.print(' '); } } private void print(String text) { out.print(text); } private void println() { out.print('\n'); } private void println(String text) { print(text); println(); } private void indentPrint(String text) { printIndent(); print(text); } private void indentPrintln(String text) { printIndent(); print(text); println(); } }