package Sequenic.T2.Obj;
/**
* A utility to show (print) the content of an object.
*
*
The show recursively traverses the structure of the
* object. Circular pointer will be marked. A maximum depth (of the
* traversal) can also be specified.
*
*
Numbers will be associated with the shown object and all its
* subobject, so that we can indicate if a (sub-) object points to an
* object that has been shown. It is possible to reset the numbering
* at each call to {@link Sequenic.T2.Obj#Show show} or to keep the
* numbering {@link Sequenic.T2.Obj#showWithContNum accross multiple calls}.
*
*
Note: showing primitive typed and boxing typed values is a
* problem. We use reflection to obtain the types of the fields, but
* typically reflection will say that a primitive typed field to have
* the corresponding boxing class. So we can't see the difference
* between the two. Currently we'll just omit the numbering
* information on these kind of values, since they will falsely signal
* for aliasing.
*/
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
import java.io.*;
import Sequenic.T2.Obj.XPP;
// import Sequenic.T2.examples.* ;
public class XShow {
/**
* As {@link Sequenic.T2.Obj said}, objects visited during the show will
* be numbered. This numbering is maintain by this pool.
*/
private Map pool = new IdentityHashMap();
/**
* Show will only go down up to this maximum depth of object
* structure. So, subobjects at the deeper depth will not be
* shown. The default is 5.
*/
public int maxDepth = 5;
/**
* The initial indentation. Default is 6.
*/
public int InitialIndentation = 6;
/**
* Turn this to true to enable internal assertions.
*/
static public boolean Debug = false;
/**
* Let C be a class. If (C,fns) is in this map, then when showing
* instances of C, only fields named in fns will be showed.
*
* Add entries to this variable to have a class with lots of fields
* shown more compactly.
*/
static public Map> showFilter =
new HashMap>();
/**
* When a class C is listed here, then when showing an instance of
* C we will only show its name (its internal state will not be
* shown at all).
*
* Add classes to this variable to surpress showing their state.
*/
static public List veryCompactShow;
// Class initialization:
static
{
//System.out.println("+++ initialization of the class XShow") ;
veryCompactShow = new LinkedList();
veryCompactShow.add(Random.class);
veryCompactShow.add(Reader.class);
veryCompactShow.add(Writer.class);
veryCompactShow.add(InputStream.class);
veryCompactShow.add(OutputStream.class);
veryCompactShow.add(RandomAccessFile.class);
veryCompactShow.add(File.class) ;
veryCompactShow.add(Class.class) ;
veryCompactShow.add(XShow.class) ;
//for (Class D : veryCompactShow) System.out.println("+++ D : " + D) ;
}
public XShow() {
}
/**
* Create a new Shower object, with the specified maximum show
* depth and initial indentation.
*/
public XShow(int maxdepth, int initialIndent) {
//assert maxdepth >= 0 && initialIndent >= 0 : "PRE" ;
maxDepth = maxdepth;
InitialIndentation = initialIndent;
}
private static String[] boxingTypeNames = {
"java.lang.Byte",
"java.lang.Integer",
"java.lang.Long",
"java.lang.Float",
"java.lang.Double",
"java.lang.Boolean",
"java.lang.Character"
};
private static boolean isBoxingType(Class C) {
boolean found = false;
for (int i = 0; i < boxingTypeNames.length && !found; i++) {
found = C.getName().equals(boxingTypeNames[i]);
}
return found;
}
/**
* Show the object o. The object numbering is maintained across
* multiple calls to this method.
*/
public String showWithContNum(Object o) {
LinkedList visited = new LinkedList();
return showWorker(o, visited, new XPP(), maxDepth).render(InitialIndentation);
}
/**
* Show the object o. The object numbering is reset at each call.
*
* @param indent Initial indentiation.
*/
public static String show(Object o, int indent, int maxDepth) {
XShow s = new XShow(maxDepth, indent);
return s.showWithContNum(o);
}
/**
* Show an object. Max-depth is 5. Initial indentation is
* 6. Object numbering is not continued over multiple calls.
*/
static public String show(Object o) {
//try {
return show(o, 6, 5);
//}
//catch (NullPointerException e){
// System.out.println("+++ " + e + ", ") ;
// e.printStackTrace() ;
// throw e ;
//}
}
// just a method to check if an object is a collection:
private static boolean isCollection(Object o) {
return Collection.class.isInstance(o) ;
}
/**
* To obtain all non-interface and non-abstract superclasses of a given
* class. The class "Object" is not included in the returned list.
*/
private static List getAllSuperClasses(Class C) {
List collected = new LinkedList();
if (// C.isInterface() || --> actually unfeasible
//Modifier.isAbstract(C.getModifiers()) ||
C.getName().equals("java.lang.Object")) {
return collected;
}
collected.add(C);
collected.addAll(getAllSuperClasses(C.getSuperclass()));
return collected;
}
/**
* Get ALL fields of a class. This includes private fields, and fields
* of the superclasses. Fields from class Objects will be excluded.
*/
private static List getAllFields(Class C) {
List result = new LinkedList() ;
List ancestors = getAllSuperClasses(C) ;
Field[] fs = null ;
int i = 0 ;
for (Class D : ancestors) {
fs = D.getDeclaredFields() ;
for (i=0; i= 0;
String s = "+++ " + (depth >= 0) ;
}
if (depth <= 0) {
return previousPP.aside_(XPP.text("..."));
}
if (o == null) {
return previousPP.aside_(XPP.text("NULL"));
}
Class C = o.getClass();
// primitive type:
if (// C.isPrimitive() || --> actually unfeasible
C.isEnum() ||
C.getName().equals("java.lang.String")) {
return previousPP.aside_(XPP.text("(" + C.getSimpleName() + ") : " + o));
}
// Bunch of standard Classes with lots of internal info,
// they will only be displayed abstractly:
// REMOVED. Now all handled in the same way using the "veryCompactShow"
// mechanism:
//if (C.getName().equals("java.lang.Class")) {
// String name = ((Class) o).getName();
// return previousPP.aside_(PP.text("(" + C.getSimpleName() + ") : " + name));
//}
//if (File.class.isAssignableFrom(C)) {
// String name = ((File) o).getName();
// return previousPP.aside_(PP.text("(" + File.class.getSimpleName() + ") : " + name));
//}
// Handle class with compacted show:
//int k = 0 ;
for(Class D : veryCompactShow) {
//if (D==null) System.out.println("+++ " + k + "-th D is null!") ;
//k++ ;
if (D.isAssignableFrom(C)) {
return previousPP.aside_(XPP.text("(" + C.getSimpleName() + ") : ..."));
}
}
// else:
boolean wasInPool = pool.containsKey(o);
if (!wasInPool) {
// o has not been taken in the pool
int newIndex = pool.size();
pool.put(o, newIndex);
}
int indexOf_o = pool.get(o);
boolean hasBeenVisited = visited.contains(o);
if (!hasBeenVisited) {
visited.add(o);
}
else return previousPP.aside_(XPP.text("(" + C.getSimpleName() + ") ---> @" + indexOf_o));
if (Debug) {
// assert visited.contains(o);
String s = "+++" + visited.contains(o);
}
// Array :
int i = 0;
if (C.isArray()) {
XPP arrayPP = XPP.text("(ARRAY) @ " + indexOf_o);
for (i = 0; i < Array.getLength(o); i++) {
// System.out.println("@" + i) ;
arrayPP.ontop(showWorker(Array.get(o, i),
visited,
XPP.text("[" + i + "]"),
depth - 1));
}
return previousPP.aside_(arrayPP);
}
// Collection:
if (isCollection(o)) {
XPP colPP = XPP.text("(" + C.getSimpleName() +
") @ " + indexOf_o);
i = 0;
for (Object element : (Collection) o) {
colPP.ontop(showWorker(element,
visited,
XPP.text("[" + i + "]"),
depth - 1));
i++;
}
return previousPP.aside_(colPP);
}
// if the object is not array nor collection:
// Box types :
if (isBoxingType(C)) {
return previousPP.aside_(XPP.text("(" + C.getSimpleName() +
")" // ") @ "
// + indexOf_o
+ " : " + o));
}
// o is of other types, and has not been visited:
// getting all C's fields, including those declared by superclasses,
// and we first remove those fields which have been specified not
// to be shown:
List allfields = getAllFields(C);
List tobeRemoved = new LinkedList();
List onlyShowTheseFields = showFilter.get(C) ;
for (Field f : allfields) {
f.setAccessible(true);
if (f.getName().equals("$assertionsDisabled")) {
tobeRemoved.add(f);
}
if (onlyShowTheseFields == null) continue ;
boolean found = false ;
for (String fn : onlyShowTheseFields) {
if (f.getName().equals(fn)) { found = true ; break ; }
}
if (!found) tobeRemoved.add(f);
}
for (Field f : tobeRemoved) {
allfields.remove(f);
}
if (Debug) {
//assert !hasBeenVisited;
String s = "+++" + !hasBeenVisited;
}
XPP titleLine = XPP.text("(" + C.getName() + ") @ " + indexOf_o);
if (allfields.isEmpty()) {
return previousPP.aside_(titleLine);
}
String fname;
// PP pp_fields = previousPP.aside_(titleLine) ;
XPP pp_fields = titleLine;
XPP entry = null;
Object fieldval = null;
i = 0;
for (Field field : allfields) {
try {
fieldval = field.get(o);
entry = XPP.text("" + field.getName());
//System.out.println(">>> " + C + "#" + field.getName() + ", declared by " + field.getDeclaringClass()) ;
if (field.getDeclaringClass() != C) {
entry = XPP.text("" +
field.getDeclaringClass().getSimpleName() + "." + field.getName());
}
pp_fields.ontop(showWorker(fieldval, visited, entry, depth - 1));
} catch (IllegalAccessException ex) {
ex.printStackTrace(System.err); // not feasible
} catch (IllegalArgumentException ex) {
ex.printStackTrace(System.err); // not feasible
}
}
return previousPP.aside_(pp_fields);
}
}