importjava.lang.reflect.Array;importjava.lang.reflect.Field;importjava.lang.reflect.Modifier;importjava.util.ArrayList;importjava.util.Arrays;importjava.util.Collections;importjava.util.HashMap;importjava.util.IdentityHashMap;importjava.util.List;importjava.util.Map;importsun.misc.Unsafe;/*** This class could be used for any object contents/memory layout printing.*/
public classClassIntrospector {private static finalUnsafe unsafe;/**Size of any Object reference*/
private static final intobjectRefSize;static{try{
Field field= Unsafe.class.getDeclaredField("theUnsafe");
field.setAccessible(true);
unsafe= (Unsafe) field.get(null);
objectRefSize= unsafe.arrayIndexScale(Object[].class);
}catch(Exception e) {throw newRuntimeException(e);
}
}/**Sizes of all primitive values*/
private static final MapprimitiveSizes;static{
primitiveSizes= new HashMap(10);
primitiveSizes.put(byte.class, 1);
primitiveSizes.put(char.class, 2);
primitiveSizes.put(int.class, 4);
primitiveSizes.put(long.class, 8);
primitiveSizes.put(float.class, 4);
primitiveSizes.put(double.class, 8);
primitiveSizes.put(boolean.class, 1);
}/*** Get object information for any Java object. Do not pass primitives to
* this method because they will boxed and the information you will get will
* be related to a boxed version of your value.
*
*@paramobj
* Object to introspect
*@returnObject info
*@throwsIllegalAccessException*/
public ObjectInfo introspect(finalObject obj)throwsIllegalAccessException {try{return introspect(obj, null);
}finally { //clean visited cache before returning in order to make//this object reusable
m_visited.clear();
}
}//we need to keep track of already visited objects in order to support//cycles in the object graphs
private IdentityHashMap m_visited = new IdentityHashMap(100);private ObjectInfo introspect(final Object obj, finalField fld)throwsIllegalAccessException {//use Field type only if the field contains null. In this case we will//at least know what's expected to be//stored in this field. Otherwise, if a field has interface type, we//won't see what's really stored in it.//Besides, we should be careful about primitives, because they are//passed as boxed values in this method//(first arg is object) - for them we should still rely on the field//type.
boolean isPrimitive = fld != null &&fld.getType().isPrimitive();boolean isRecursive = false; //will be set to true if we have already//seen this object
if (!isPrimitive) {if(m_visited.containsKey(obj)) {
isRecursive= true;
}
m_visited.put(obj,true);
}final Class type = (fld == null || (obj != null && !isPrimitive)) ?obj
.getClass() : fld.getType();int arraySize = 0;int baseOffset = 0;int indexScale = 0;if (type.isArray() && obj != null) {
baseOffset=unsafe.arrayBaseOffset(type);
indexScale=unsafe.arrayIndexScale(type);
arraySize= baseOffset + indexScale *Array.getLength(obj);
}finalObjectInfo root;if (fld == null) {
root= new ObjectInfo("", type.getCanonicalName(), getContents(obj,
type),0, getShallowSize(type), arraySize, baseOffset,
indexScale);
}else{final int offset = (int) unsafe.objectFieldOffset(fld);
root= newObjectInfo(fld.getName(), type.getCanonicalName(),
getContents(obj, type), offset, getShallowSize(type),
arraySize, baseOffset, indexScale);
}if (!isRecursive && obj != null) {if(isObjectArray(type)) {//introspect object arrays
final Object[] ar =(Object[]) obj;for (finalObject item : ar)if (item != null) {
root.addChild(introspect(item,null));
}
}else{for (finalField field : getAllFields(type)) {if ((field.getModifiers() & Modifier.STATIC) != 0) {continue;
}
field.setAccessible(true);
root.addChild(introspect(field.get(obj), field));
}
}
}
root.sort();//sort by offset
returnroot;
}//get all fields for this class, including all superclasses fields
private static List getAllFields(finalClass type) {if(type.isPrimitive()) {returnCollections.emptyList();
}
Class cur=type;final List res = new ArrayList(10);while (true) {
Collections.addAll(res, cur.getDeclaredFields());if (cur == Object.class) {break;
}
cur=cur.getSuperclass();
}returnres;
}//check if it is an array of objects. I suspect there must be a more//API-friendly way to make this check.
private static boolean isObjectArray(finalClass type) {if (!type.isArray()) {return false;
}if (type == byte[].class || type == boolean[].class
|| type == char[].class || type == short[].class
|| type == int[].class || type == long[].class
|| type == float[].class || type == double[].class) {return false;
}return true;
}//advanced toString logic
private static String getContents(final Object val, finalClass type) {if (val == null) {return "null";
}if(type.isArray()) {if (type == byte[].class) {return Arrays.toString((byte[]) val);
}else if (type == boolean[].class) {return Arrays.toString((boolean[]) val);
}else if (type == char[].class) {return Arrays.toString((char[]) val);
}else if (type == short[].class) {return Arrays.toString((short[]) val);
}else if (type == int[].class) {return Arrays.toString((int[]) val);
}else if (type == long[].class) {return Arrays.toString((long[]) val);
}else if (type == float[].class) {return Arrays.toString((float[]) val);
}else if (type == double[].class) {return Arrays.toString((double[]) val);
}else{returnArrays.toString((Object[]) val);
}
}returnval.toString();
}//obtain a shallow size of a field of given class (primitive or object//reference size)
private static int getShallowSize(finalClass type) {if(type.isPrimitive()) {final Integer res =primitiveSizes.get(type);return res != null ? res : 0;
}else{returnobjectRefSize;
}
}
}