|
||||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |
java.lang.Objectbb.util.ObjectState
public class ObjectState
Determines the state of some other object.
The constructor finds the complete state: every Field in object's Class, all of its superclasses, and its complete interface hierarchy. (Recall: interfaces can declare static final fields, altho this is not a good idea).
By default, the constructor stores all this state,
however, an optional number of ObjectState.Filter
s may be provided to restrict the state that is stored.
Many users will find the pre-defined immediateState
or immediateInstanceState
fields convenient.
The state of this other object is stored as a Class --> Field[] map.
Its accessor is getClassToFields
.
This class offers convenience toString()
and toStringLabeled()
methods,
which return formatted Strings and make it trivial for any class to write a generic toString/toStringLabeled methods.
For example,
@Override public String toString() {
return new ObjectState
( this, ObjectState.immediateInstanceState
).toString()
;
}
or
public String toStringLabeled() {
return new ObjectState
( this ).toStringLabeled()
;
}
might work.
Even more convenient for the casual user is the static describe(java.lang.Object)
method.
If this generic toString/toStringLabeled approach is inadequate,
generateToStringCode
/generateToStringLabeledCode
may be used to automatically generate java source code which can then be customized.
For example, a call like:
System.out.println( new ObjectState
( this, immediateState
).generateToStringCode
() );
possibly followed by some moderate hand editing might be all that is required.
This class is useful in debugging, and is heavily used by the p
and g
classes.
This class uses reflection to get the Field data. Since reflection is much slower than direct field access, care should be taken in using this class if high performance is needed.
This class is not multithread safe.
Nested Class Summary | |
---|---|
static class |
ObjectState.AcceptOnlyImmediateClass
Accepts a Class only if it is object's class. |
static class |
ObjectState.AcceptOnlyPublicProtectedFields
Accepts a Field only if it is public or protected. |
static class |
ObjectState.ClassFilterAbstract
Base class for Filters which care only about Classes. |
static class |
ObjectState.FieldFilterAbstract
Base class for Filters which care only about Fields. |
static interface |
ObjectState.Filter
Interface for types which accept/reject Classes /Fields . |
static class |
ObjectState.RejectInterfaces
Rejects a Class if it is an interface. |
static class |
ObjectState.RejectObjectClass
Rejects a Class if it is Object.class. |
static class |
ObjectState.RejectStaticFields
Rejects a Field if it is static. |
static class |
ObjectState.UnitTest
See the Overview page of the project's javadocs for a general description of this unit test class. |
Field Summary | |
---|---|
private Map<Class,Field[]> |
classToFields
For each Class in object 's hierarchy (i.e. object's direct Class, any superclasses, any interface hierarchy),
stores a mapping from the Class to an array of every declared Field of that class. |
private Set<ObjectState.Filter> |
filters
Stores all the ObjectState.Filter s that this instance uses. |
static Set<ObjectState.Filter> |
immediateInstanceState
A Set of ObjectState.Filter s that acts to only accept this state:
fields from the immediate object under consideration (i.e. reject fields from superclasses/interfaces)
instance fields (i.e. reject static fields)
Contract: is never null. |
static Set<ObjectState.Filter> |
immediateState
A Set of ObjectState.Filter s that acts to only accept fields from the immediate object under consideration
(i.e. reject fields from superclasses/interfaces); both static and instance fields are accepted. |
private Object |
object
Object that this instance is concerned with. |
private static ThreadLocal<Map<Object,Integer>> |
threadToObjCount
The toStringLabeled(String, String) method can result in recursive calls
because of its use of toStringSmart(java.lang.Object, java.lang.String, java.lang.String, java.util.Set . |
Constructor Summary | |
---|---|
ObjectState(Object object,
ObjectState.Filter... filters)
Calls the fundamental constructor after converting filters into a non-null Set. |
|
ObjectState(Object object,
Set<ObjectState.Filter> filters)
Constructor. |
Method Summary | |
---|---|
private void |
appendCodeForClass(Class c,
StringBuilder sb,
AtomicBoolean first,
boolean labeled)
|
private void |
appendField(Field field,
StringBuilder sb,
String prefix,
String indent)
|
private void |
appendFields(Class c,
StringBuilder sb,
String prefix,
String indent)
|
private boolean |
classAccepted(Class c,
Object object,
Set<ObjectState.Filter> filters)
|
private static void |
decrementObjectGraph(Object obj)
|
static String |
describe(Object obj)
Returns . |
static String |
describe(Object obj,
String prefix)
Returns new |
private Field[] |
extractFields(Class c,
Object object,
Set<ObjectState.Filter> filters)
|
private boolean |
fieldAccepted(Field field,
Object object,
Set<ObjectState.Filter> filters)
|
private String |
fieldErrors(Field field)
|
private String |
fieldWarnings(Field field)
|
private String |
generateCode(boolean labeled)
|
String |
generateToStringCode()
Returns a String which is valid java source code for a toString implementation for object . |
String |
generateToStringLabeledCode()
Returns a String which is valid java source code for a toStringLabeled implementation for object . |
Map<Class,Field[]> |
getClassToFields()
Accessor for classToFields . |
Set<ObjectState.Filter> |
getFilters()
Accessor for filters . |
int |
getHashCode()
Returns 0 if returns null. |
int |
getNumberOfFields()
Returns the total number of fields stored in all the values of classToFields . |
Object |
getObject()
Accessor for object . |
String |
getType()
Returns getObject 's class name. |
private void |
handleClass(Class c,
Object object,
Set<ObjectState.Filter> filters,
Map<Class,Field[]> map)
|
private static void |
incrementObjectGraph(Object obj)
|
private static boolean |
isInObjectGraph(Object obj)
|
private static boolean |
isObjectGraphEmpty()
|
private String |
lineStart(AtomicBoolean first,
boolean labeled)
|
String |
toString()
Returns . |
String |
toString(String separator)
Returns a simple String which barely describes the data in this class compared to toStringLabeled . |
String |
toStringLabeled()
Returns . |
String |
toStringLabeled(int indentLevel)
Returns
|
String |
toStringLabeled(String prefix,
String indent)
Returns a complex String which more fully describes the data in this class compared to toString(String) . |
static String |
toStringSmart(Object obj,
String prefix,
String indent,
Set<ObjectState.Filter> filters)
Returns a String which describes the state of obj. |
Methods inherited from class java.lang.Object |
---|
clone, equals, finalize, getClass, hashCode, notify, notifyAll, wait, wait, wait |
Field Detail |
---|
private static final ThreadLocal<Map<Object,Integer>> threadToObjCount
toStringLabeled(String, String)
method can result in recursive calls
because of its use of toStringSmart(java.lang.Object, java.lang.String, java.lang.String, java.util.Set)
.
This means that infinite loops can result when applied to certain object graphs
with circular reference chains, just as with object serialization.
One way to prevent this is to keep track of objects encountered so far,
and use a placeholder if ever encounter a known object and stop the recursion there.
That is what this field does: it allows the thread executing toStringLabeled to know what objects it has encountered so far.
It is a Map from an encountered Object to the number of times that it has been encountered.
Contract: is never null.
The Map implementation used in the values must use reference equality, not object-equality; see IdentityHashMap
.
A given thread's Map must be empty when the topmost toStringLabeled call finally returns.
Note that this field must be static and not instance based, because toStringSmart can create new ObjectState intances, therefore the tracking has to be done beyond the instance level.
public static final Set<ObjectState.Filter> immediateState
ObjectState.Filter
s that acts to only accept fields from the immediate object under consideration
(i.e. reject fields from superclasses/interfaces); both static and instance fields are accepted.
Contract: is never null.
Furthermore, is unmodifiable
in order to preserve encapsulation.
It will never be changed after class initialization, so users may safely iterate over it with no fear of ConcurrentModificationException
.
public static final Set<ObjectState.Filter> immediateInstanceState
ObjectState.Filter
s that acts to only accept this state:
Contract: is never null.
Furthermore, is unmodifiable
in order to preserve encapsulation.
It will never be changed after class initialization, so users may safely iterate over it with no fear of ConcurrentModificationException
.
private final Object object
Contract: none; may even be null.
private final Set<ObjectState.Filter> filters
ObjectState.Filter
s that this instance uses.
If multiple elements are present, then each is connected by an implicit AND
(i.e. a Class/Field must be accepted by every Filter in order to be be present in classToFields
).
Contract: is never null, but may be empty.
Furthermore, is unmodifiable
in order to preserve encapsulation.
It will never be changed after construction, so users may safely iterate over it with no fear of ConcurrentModificationException
.
private final Map<Class,Field[]> classToFields
object
's hierarchy (i.e. object's direct Class, any superclasses, any interface hierarchy),
stores a mapping from the Class to an array of every declared Field of that class.
Contract:
object
is nullReflectUtil.FieldComparator
.
isAccessible
method originally returned false,
then a call to field.setAccessible
(true) is made during construction.
This means that all code in this class can count on these elements being accessible
and should never susequently call setAccessible.
It also means that no code in this class should ever assume that an element's isAccessible
result says anything about the original accessibility of the field.
unmodifiable
in order to preserve encapsulation.
It will never be changed after construction, so users may safely iterate over it with no fear of ConcurrentModificationException
.
Constructor Detail |
---|
public ObjectState(Object object, ObjectState.Filter... filters) throws SecurityException
fundamental constructor
after converting filters into a non-null Set.
SecurityException
public ObjectState(Object object, Set<ObjectState.Filter> filters) throws SecurityException
classToFields
.
object
- assigned to object
, so must satisfy the contractfilters
- assigned to filters
, so must satisfy the contract
SecurityException
- if Class.getDeclaredFields
has an isssueMethod Detail |
---|
private static boolean isInObjectGraph(Object obj)
private static boolean isObjectGraphEmpty()
private static void incrementObjectGraph(Object obj)
private static void decrementObjectGraph(Object obj)
public static String describe(Object obj) throws RuntimeException
describe
( obj, null)
.
RuntimeException
public static String describe(Object obj, String prefix) throws RuntimeException
new ObjectState
( obj, immediateInstanceState
).toStringLabeled()
( prefix, "\t" )
.
Is simply a convenience method that covers a conmmon usage scenario.
obj
- the object to get a report on its immediate instance state
RuntimeException
- (or some subclass) if any problem occurs; this RuntimeException may wrap some underlying Throwablepublic static String toStringSmart(Object obj, String prefix, String indent, Set<ObjectState.Filter> filters) throws RuntimeException
The following special cases are checked in the order listed:
Collection
, or Map
, then each element is printed on its own indented line
(altho if it is empty, then the text "<EMPTY (XXX has no elements)>" is returned,
where XXX is either array, Collection, or Map as appropriate)
java.lang
package
and/or is an instance of CharSequence
,
then obj.toString() is returned
(the idea here being that the toString method for these types is likely to produce a simple 1 line result)
new ObjectState( obj, getFilters()
).toStringLabeled(prefix, indent)
(i.e. a recursive call).
RuntimeException
- (or some subclass) if any problem occurs; this RuntimeException may wrap some underlying Throwable, so be sure to inspect its causeprivate void handleClass(Class c, Object object, Set<ObjectState.Filter> filters, Map<Class,Field[]> map)
private boolean classAccepted(Class c, Object object, Set<ObjectState.Filter> filters)
private Field[] extractFields(Class c, Object object, Set<ObjectState.Filter> filters) throws SecurityException
SecurityException
private boolean fieldAccepted(Field field, Object object, Set<ObjectState.Filter> filters)
public Object getObject()
object
.
public Set<ObjectState.Filter> getFilters()
filters
.
public Map<Class,Field[]> getClassToFields()
classToFields
.
public String getType()
getObject
's class name.
In general, the result is getObject's class name, however, the following are special cases:
public int getHashCode()
getObject()
returns null.
Else returns getObject().hashCode()
.
public int getNumberOfFields() throws IllegalStateException
classToFields
.
IllegalStateException
- if getClassToFields
raises itpublic String toString() throws RuntimeException
toString
("\t")
.
toString
in class Object
RuntimeException
public String toString(String separator) throws RuntimeException
toStringLabeled
.
The result always consists of but a single line which has all the fields of object
recorded by this instance.
These fields appear only as their values (e.g. no field name labels are present),
with the value's toString method (from whatever class it belongs to) being used to generate the String form of the value.
If a null reference is ever encountered, it is represented by the text "null".
The result is likely to be very confusing unless filters
is very restrictive
(e.g. is something like immediateInstanceState
).
In contrast, toStringLabeled can handle any scenario, since it labels its result.
Contract: the result is never blank and only ends with a newline if that newline is from the last field's value.
separator
- text used to separate the fields inthe result; may not be null
RuntimeException
- (or some subclass) if any problem occurs; this RuntimeException may wrap some underlying Throwablepublic String toStringLabeled() throws RuntimeException
toStringLabeled
(0)
.
RuntimeException
public String toStringLabeled(int indentLevel) throws RuntimeException
toStringLabeled
( StringUtil.getTabs
.(indentLevel), "\t" )
.
RuntimeException
public String toStringLabeled(String prefix, String indent) throws RuntimeException
toString(String)
.
The result always consists of multiple lines, with a given line dedicated to each piece of information.
The first line describes the calling thread.
The second line reports getHashCode()
.
Finally, the remaining lines list all the fields of object
recorded by this instance.
These fields appear in the form "name = value",
with toStringSmart(java.lang.Object, java.lang.String, java.lang.String, java.util.Set
being used to generate the String form of the value.
The class that a field was declared in actually appears first on its own line before its declared fields are listed.
Contract: the result is never blank and always ends with a newline.
prefix
- optional text that appears at the start of every line in the result; may be nullindent
- text used to indent by one level; may not be null
RuntimeException
- (or some subclass) if any problem occurs; this RuntimeException may wrap some underlying Throwable, so be sure to inspect its causeprivate void appendFields(Class c, StringBuilder sb, String prefix, String indent) throws Exception
Exception
private void appendField(Field field, StringBuilder sb, String prefix, String indent) throws Exception
Exception
public String generateToStringCode()
object
.
The result contains every field present in classToFields
.
Each such field is given its own line in the source code,
but the result that the source code produces has every field in a single tab delimited line.
If a field's access level is such that its direct access will cause an IllegalAccessException, then the line for that field is actually commented out and a diagnostic error line is printed beneath it which explains the issue and possibly offers resolution (the programmer will have to manually intervene).
If a field is a static, then a warning comment is placed at the end of its line suggesting that the line be removed.
The programer is highly likely to need to hand edit the result before using it in an actual class:
the warnings and errors mentioned above should be dealt with,
custom decisions may need be made about how to handle individual fields, etc.
This method merely helps automate the process of writing a custom toString method
for someone who does not want to use the generic toString
(e.g. to obtain more customization or performance).
Contract: the result is never blank and always ends with a newline.
public String generateToStringLabeledCode()
object
.
The result contains every field present in classToFields
.
Each such field is given its own line in the result,
both in the source code as well as the result that the source code produces.
If a field's access level is such that its direct access will cause an IllegalAccessException, then the line for that field is actually commented out and a diagnostic error line is printed beneath it which explains the issue and possibly offers resolution (the programmer will have to manually intervene).
If a field is a static, then a warning comment is placed at the end of its line suggesting that the line be removed.
The programer is highly likely to need to hand edit the result before using it in an actual class:
the warnings and errors mentioned above should be dealt with,
custom decisions may need be made about how to handle individual fields, etc.
This method merely helps automate the process of writing a custom toStringLabeled method
for someone who does not want to use the generic toStringLabeled
(e.g. to obtain more customization or performance).
Contract: the result is never blank and always ends with a newline.
private String generateCode(boolean labeled)
private void appendCodeForClass(Class c, StringBuilder sb, AtomicBoolean first, boolean labeled)
private String lineStart(AtomicBoolean first, boolean labeled)
private String fieldErrors(Field field)
private String fieldWarnings(Field field)
|
||||||||||
PREV CLASS NEXT CLASS | FRAMES NO FRAMES | |||||||||
SUMMARY: NESTED | FIELD | CONSTR | METHOD | DETAIL: FIELD | CONSTR | METHOD |