/** * */ package net.varkhan.base.containers; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.HashSet; import java.util.Set; /** * <b>Reflection tools.</b> * <p/> * Reflexive object structure examination, and heap monitoring tools. * <p/> * * @author varkhan * @date Mar 12, 2009 * @time 8:06:12 PM */ @SuppressWarnings( { "UnusedDeclaration" }) public final class Reflect { /** * Private empty constructor that forbids instantiation of this class. */ protected Reflect() { } /********************************************************************************** ** Memory footprint computation for primitive types **/ /** The size of a Java void */ private static final long SIZE_VOID = 0L; /** The size of a Java boolean -- note that this is hardly well defined by the JLS */ private static final long SIZE_BOOL = 1L; /** The size of a Java byte */ private static final long SIZE_BYTE = Byte.SIZE>>3; /** The size of a Java char */ private static final long SIZE_CHAR = Character.SIZE>>3; /** The size of a Java short */ private static final long SIZE_SHORT = Short.SIZE>>3; /** The size of a Java int */ private static final long SIZE_INT = Integer.SIZE>>3; /** The size of a Java long */ private static final long SIZE_LONG = Long.SIZE>>3; /** The size of a Java float */ private static final long SIZE_FLOAT = Float.SIZE>>3; /** The size of a Java double */ private static final long SIZE_DOUBLE = Double.SIZE>>3; /** * Returns the number of bytes used by a primitive type. * * @param cls the primitive type class * @return the number of bytes used in memory by the type represented by the argument class */ public static long sizeOfClass(Class<?> cls) { if(cls==null) return 0L; if(cls==Class.class) return 0L; if(cls==Void.TYPE) return SIZE_VOID; if(cls==Boolean.TYPE) return SIZE_BOOL; if(cls==Byte.TYPE) return SIZE_BYTE; if(cls==Character.TYPE) return SIZE_CHAR; if(cls==Short.TYPE) return SIZE_SHORT; if(cls==Integer.TYPE) return SIZE_INT; if(cls==Long.TYPE) return SIZE_LONG; if(cls==Float.TYPE) return SIZE_FLOAT; if(cls==Double.TYPE) return SIZE_DOUBLE; return 0L; } /** * Returns the number of bytes used by the primitive type argument. * * @param val the type * @return the number of bytes this type uses in memory */ public static long sizeOf(boolean val) { return SIZE_BOOL; } /** * Returns the number of bytes used by the primitive type argument. * * @param val the type * @return the number of bytes this type uses in memory */ public static long sizeOf(byte val) { return SIZE_BYTE; } /** * Returns the number of bytes used by the primitive type argument. * * @param val the type * @return the number of bytes this type uses in memory */ public static long sizeOf(char val) { return SIZE_CHAR; } /** * Returns the number of bytes used by the primitive type argument. * * @param val the type * @return the number of bytes this type uses in memory */ public static long sizeOf(short val) { return SIZE_SHORT; } /** * Returns the number of bytes used by the primitive type argument. * * @param val the type * @return the number of bytes this type uses in memory */ public static long sizeOf(int val) { return SIZE_INT; } /** * Returns the number of bytes used by the primitive type argument. * * @param val the type * @return the number of bytes this type uses in memory */ public static long sizeOf(long val) { return SIZE_LONG; } /** * Returns the number of bytes used by the primitive type argument. * * @param val the type * @return the number of bytes this type uses in memory */ public static long sizeOf(float val) { return SIZE_FLOAT; } /** * Returns the number of bytes used by the primitive type argument. * * @param val the type * @return the number of bytes this type uses in memory */ public static long sizeOf(double val) { return SIZE_DOUBLE; } /********************************************************************************** ** Reference class **/ /** * <b>A reference to an object.</b> * <p> * This class overrides standard object equality tests to * provide comparisons on object addresses rather than * object contents. * <p/> * @param <Type> the type of the object being referenced */ public static class Ref<Type> { public final Type ref; public Ref(Type obj) { this.ref = obj; } public int hashCode() { return System.identityHashCode(ref); } @SuppressWarnings("unchecked") public boolean equals(Object obj) { return obj.getClass()==Ref.class && ref==((Ref)obj).ref; } public String toString() { String name = ref.getClass().getName(); StringBuilder buf = new StringBuilder(name.length()+8); buf.append("#").append(name); buf.append("@").append(Integer.toHexString(System.identityHashCode(ref))); return buf.toString(); } } /********************************************************************************** ** Memory footprint computation for generic objects **/ private static final long SIZE_OBJ = 8; private static final long SIZE_REF = 4; /** * Computes the size of a particular class of objects, discarding any * contribution from objects already in the reference context. * * @param obj the object * @return the size, in bytes, of the object */ public static long sizeOf(Object obj) { return sizeOf(obj, new HashSet<Ref<?>>()); } /** * Computes the size of a particular class of objects, discarding any * contribution from objects already in the reference context. * * @param obj the object * @param ctx the reference context (or {@literal null} if no context should be used) * @return the size, in bytes, of the object */ public static long sizeOf(final Object obj, final Set<Ref<?>> ctx) { if(obj==null) return 0; Class<?> cls = obj.getClass(); if(cls==Class.class) return 0; if(cls.isPrimitive()) return sizeOfClass(cls); if(ctx!=null && !ctx.add(new Reflect.Ref<Object>(obj))) return 0; if(cls.isArray()) { cls = obj.getClass().getComponentType(); if(cls==null) return 0; // This should not happen... int length = Array.getLength(obj); if(cls.isPrimitive()) { return SIZE_OBJ + sizeOfClass(Integer.TYPE) + length * sizeOfClass(cls); } else { long size = SIZE_OBJ + sizeOfClass(Integer.TYPE) + SIZE_REF * length; for(int i = 0; i < length; i++) { size += sizeOf(Array.get(obj, i), ctx); } return size; } } long size = SIZE_OBJ; while(cls!=null && cls!=Object.class) { for(final Field fld: cls.getDeclaredFields()) { if((fld.getModifiers() & Modifier.STATIC) == 0) { final Class<?> fcls = fld.getType(); if(fcls.isPrimitive()) { size += sizeOfClass(fcls); } else { size += SIZE_REF; // Use the doPrivileged method of the access controller // to bypass the private and protected modifiers Object val = java.security.AccessController.doPrivileged( new java.security.PrivilegedAction<Object>() { public Object run() { try { fld.setAccessible(true); return fld.get(obj); } catch(Exception e) { return null; } finally { fld.setAccessible(false); } } } ); if(val!=null) size += sizeOf(val, ctx); } } } cls = cls.getSuperclass(); } return size; } /********************************************************************************** ** Field class **/ /** * An object's field specifications. * * @param <Type> the field type * @see Reflect#visit */ public static class Fld<Type> { public final Class<Type> cls; public final String fld; public final Type val; public Fld(Class<Type> cls, String fld, Type val) { this.cls=cls; this.fld=fld; this.val=val; } /** * The field's class * * @return the class of the field */ public Class<Type> fieldClass() { return cls; } /** * The field's name * * @return the name of the field */ public String fieldName() { return fld; } /** * The field's value * * @return the value of the field */ public Type fieldValue() { return val; } } /********************************************************************************** ** Object structure traversal **/ /** * Calls a visitor for every field of an object, until the visitor returns a negative value. * * @param obj the object * @param vis the visitor * @param par a control parameter * @param <Par> the type of the control parameter * * @return the sum of all non-negative values returned by the visitor, or {@literal -1L} if an error occurred * @see Fld */ @SuppressWarnings( { "unchecked" }) public static <Par> long visit(final Object obj, final Visitable.Visitor<Fld<Object>,Par> vis, Par par) { if(obj==null) return -1L; Class ocl = obj.getClass(); if(ocl==Class.class) return 0L; if(ocl.isPrimitive()) return 0L; long value = 0L; // Climb up the inheritance hierarchy while(ocl!=null && ocl!=Object.class) { // If object is an array, iterate through length, then all elements if(ocl.isArray()) { final int length = Array.getLength(obj); long v = vis.invoke(new Fld(int.class, "length", length), par); if(v<0) return value; value += v; Class cls = obj.getClass().getComponentType(); if(cls==null) return -1L; for(int i=0; i<length; i++) { Object val = Array.get(obj, i); v = vis.invoke(new Fld(cls, "["+i+"]", val), par); if(v<0) return value; value += v; } } // If object is a normal object, iterate through declared fields else for(final Field fld: ocl.getDeclaredFields()) { if((fld.getModifiers() & Modifier.STATIC) == 0) { Class cls = fld.getType(); // Use the doPrivileged method of the access controller // to bypass the private and protected modifiers Object val = java.security.AccessController.doPrivileged( new java.security.PrivilegedAction<Object>() { public Object run() { try { fld.setAccessible(true); return fld.get(obj); } catch(Exception e) { return null; } finally { fld.setAccessible(false); } } } ); long v = vis.invoke(new Fld(cls, fld.getName(), val), par); if(v<0) return value; value += v; } } // Jump to superclass, and visit again ocl = ocl.getSuperclass(); } return value; } }