001: package U2.T2.Obj;
002:
003: /**
004: * A utility to show (print) the content of an object.
005: *
006: * <p>The show recursively traverses the structure of the
007: * object. Circular pointer will be marked. A maximum depth (of the
008: * traversal) can also be specified.
009: *
010: * <p>Numbers will be associated with the shown object and all its
011: * subobject, so that we can indicate if a (sub-) object points to an
012: * object that has been shown. It is possible to reset the numbering
013: * at each call to {@link U2.T2.Obj#Show show} or to keep the
014: * numbering {@link U2.T2.Obj#showWithContNum accross multiple calls}.
015: *
016: * <p>Note: showing primitive typed and boxing typed values is a
017: * problem. We use reflection to obtain the types of the fields, but
018: * typically reflection will say that a primitive typed field to have
019: * the corresponding boxing class. So we can't see the difference
020: * between the two. Currently we'll just omit the numbering
021: * information on these kind of values, since they will falsely signal
022: * for aliasing.
023: */
024:
025: import java.lang.reflect.*;
026: import java.util.*;
027: import U2.P2.*;
028: import U2.T2.Msg.*;
029: import U2.T2.Reflection.*;
030:
031: // import U2.T2.examples.* ;
032:
033: public class Show {
034:
035: /**
036: * As {@link U2.T2.Obj said}, objects visited during the show will
037: * be numbered. This numbering is maintain by this pool.
038: */
039: private HashMap<Object, Integer> pool;
040:
041: /**
042: * Show will only go down up to this maximum depth of object
043: * structure. So, subobjects at the deeper depth will not be
044: * shown. The default is 5.
045: */
046: private int maxDepth = 5;
047:
048: /**
049: * The initial indentation. Default is 6.
050: */
051: private int InitialIndentation = 6;
052:
053: public Show() {
054: pool = new HashMap<Object, Integer>();
055: }
056:
057: /**
058: * Create a new Shower object, with the specified maximum show
059: * depth and initial indentation.
060: */
061: public Show(int maxdepth, int initialIndent) {
062: pool = new HashMap<Object, Integer>();
063: maxDepth = maxdepth;
064: InitialIndentation = initialIndent;
065: }
066:
067: private static String[] boxingTypeNames = { "java.lang.Byte",
068: "java.lang.Integer", "java.lang.Long", "java.lang.Float",
069: "java.lang.Double", "java.lang.Boolean",
070: "java.lang.Character" };
071:
072: private static boolean isBoxingType(Class C) {
073: boolean found = false;
074: for (int i = 0; i < boxingTypeNames.length && !found; i++)
075: found = C.getName().equals(boxingTypeNames[i]);
076: return found;
077: }
078:
079: /**
080: * Show the object o. The object numbering is maintained across
081: * multiple calls to this method.
082: */
083: public String showWithContNum(Object o) {
084: LinkedList visited = new LinkedList();
085: return showWorker(o, visited, new PP(), maxDepth).render(
086: InitialIndentation);
087: }
088:
089: /**
090: * Show the object o. The object numbering is reset at each call.
091: *
092: * @param indent Initial indentiation.
093: */
094: public static String show(Object o, int indent, int maxDepth) {
095: Show s = new Show(maxDepth, indent);
096: return s.showWithContNum(o);
097: }
098:
099: /**
100: * Show an object. Max-depth is 5. Initial indentation is
101: * 6. Object numbering is not continued over multiple calls.
102: */
103: static public String show(Object o) {
104: return show(o, 6, 5);
105: }
106:
107: // just a method to check if an object is a collection:
108: private static boolean isCollection(Object o) {
109: try {
110: return Class.forName("java.util.Collection").isInstance(o);
111: } catch (Exception e) {
112: }
113: return false;
114: }
115:
116: // the worker function for show:
117: private PP showWorker(Object o, Collection visited, PP previousPP,
118: int depth)
119:
120: {
121: if (depth <= 0)
122: return previousPP.aside_(PP.text("..."));
123:
124: if (o == null)
125: return previousPP.aside_(PP.text("NULL"));
126:
127: Class C = o.getClass();
128:
129: // primitive type:
130: if (C.isPrimitive() || C.isEnum()
131: || C.getName().equals("java.lang.String"))
132:
133: return previousPP.aside_(PP.text("(" + C.getSimpleName()
134: + ") : " + o));
135:
136: // else it is an object;
137:
138: if (!pool.containsKey(o)) {
139: // o has not been taken in the pool
140: int newIndex = pool.size();
141: pool.put(o, newIndex);
142: }
143:
144: int indexOf_o = pool.get(o);
145:
146: boolean hasBeenVisited = true;
147: if (!visited.contains(o)) {
148: hasBeenVisited = false;
149: visited.add(o);
150: }
151:
152: // Array :
153: if (C.isArray()) {
154: PP arrayPP = PP.text("(ARRAY) @ " + indexOf_o);
155: for (int i = 0; i < Array.getLength(o); i++) {
156: // System.out.println("@" + i) ;
157: arrayPP.ontop(showWorker(Array.get(o, i), visited, PP
158: .text("[" + i + "]"), depth - 1));
159: }
160: return previousPP.aside_(arrayPP);
161:
162: //Message.console(Message.DEVEL_WARNING,"Cannot show an array.",new Show()) ;
163: //return PP.aside(previousPP, PP.text("...some array")) ;
164: }
165:
166: // Collection:
167: if (isCollection(o)) {
168: PP colPP = PP.text("(" + C.getSimpleName() + ") @ "
169: + indexOf_o);
170: int i = 0;
171: for (Object element : (Collection) o) {
172: colPP.ontop(showWorker(element, visited, PP.text("["
173: + i + "]"), depth - 1));
174: i++;
175: }
176: return previousPP.aside_(colPP);
177:
178: }
179:
180: // if the object is not array nor collection:
181:
182: // Box types :
183:
184: if (isBoxingType(C))
185:
186: return previousPP.aside_(PP.text("(" + C.getSimpleName()
187: + ")"
188: // ") @ "
189: // + indexOf_o
190: + " : " + o));
191:
192: // else o is an object with fields :
193:
194: if (hasBeenVisited)
195: return previousPP.aside_(PP.text("(" + C.getSimpleName()
196: + ") ---> @" + indexOf_o));
197: // o has not been visited:
198: // getting all C's fields, including those declared by superclasses:
199: LinkedList<Field> allfields = new LinkedList<Field>();
200: List<Class> classes = ReflUtil.getAllSuperClasses(C);
201: classes.add(C);
202: for (Class D : classes) {
203: Field[] fields = D.getDeclaredFields();
204: for (int i = 0; i < fields.length; i++) {
205: if (!fields[i].getName().equals("$assertionsDisabled")) {
206: fields[i].setAccessible(true);
207: allfields.add(fields[i]);
208: }
209: }
210: }
211:
212: PP titleLine = PP.text("(" + C.getName() + ") @ " + indexOf_o);
213:
214: if (allfields.isEmpty())
215: return previousPP.aside_(titleLine);
216:
217: String fname;
218: PP pp_fields = previousPP.aside_(titleLine);
219: PP entry = null;
220: Object fieldval = null;
221: int i = 0;
222:
223: for (Field field : allfields) {
224:
225: try {
226: fieldval = field.get(o);
227: entry = PP.text(" " + field.getName());
228: if (field.getDeclaringClass() != C)
229: entry = PP.text(" "
230: + field.getDeclaringClass().getSimpleName()
231: + "." + field.getName());
232: pp_fields.ontop(showWorker(fieldval, visited, entry,
233: depth - 1));
234: } catch (IllegalAccessException ex) {
235: ex.printStackTrace(System.out);
236: } catch (IllegalArgumentException ex) {
237: ex.printStackTrace(System.out);
238: }
239:
240: }
241:
242: return pp_fields;
243:
244: }
245:
246: // test:
247: static public void main(String[] args) {
248:
249: System.out.println(show(new Integer(100)));
250: System.out.println(show("Hello ET!"));
251: System.out.println(show(new int[2]));
252:
253: // System.out.println(show(new B3())) ;
254:
255: /*
256: MyList xs = new MyList() ;
257: xs.insert(1) ; xs.insert(-1) ;
258: System.out.println(show(xs)) ;
259: xs = new MyList() ;
260: xs.insert(0) ; xs.list.next = xs.list ;
261: System.out.println(show(xs)) ;
262: */
263: }
264:
265: }
|