Please wait. This can take some minutes ...
Many resources are needed to download a project. Please understand that we have to compensate our server costs. Thank you in advance.
Project price only 1 $
You can buy this project and download/modify it how often you want.
org.exparity.beans.InstanceInspector Maven / Gradle / Ivy
Go to download
A Java library of bean utilities for manipulating and inspecting Java classes implementing the Java Beans standard
package org.exparity.beans;
import java.lang.reflect.Array;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.exparity.beans.naming.CamelCaseNamingStrategy;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import static java.lang.System.identityHashCode;
import static org.exparity.beans.InstanceInspector.InspectionDepth.DEEP;
import static org.exparity.beans.InstanceInspector.Overflow.DENY_OVERFLOW;
import static org.exparity.beans.Type.type;
/**
* Helper class which inspects the bean and exposes the properties of the bean to support the visitor pattern
*
* @author Stewart Bissett
*/
class InstanceInspector {
enum InspectionDepth {
SHALLOW, DEEP
};
enum Overflow {
ALLOW_OVERFLOW, DENY_OVERFLOW
};
static InstanceInspector beanInspector() {
return new InstanceInspector(InspectionDepth.SHALLOW, Overflow.DENY_OVERFLOW);
}
static InstanceInspector graphInspector() {
return new InstanceInspector(InspectionDepth.DEEP, Overflow.DENY_OVERFLOW);
};
private static final Logger LOG = LoggerFactory.getLogger(InstanceInspector.class);
private static final Integer OBJECT_HITS_BEFORE_OVERFLOW = 0;
private final ThreadLocal> inspected = new ThreadLocal>() {
@Override
protected Map initialValue() {
return new HashMap();
}
};
private final InspectionDepth depth;
private final Overflow overflow;
InstanceInspector(final InspectionDepth depth, final Overflow overflow) {
this.depth = depth;
this.overflow = overflow;
}
/**
* Inspect the supplied object and fire callbacks on the supplied {@link BeanVisitor} for every property exposed on the object
*
* @param instance an object instance to inspect for Java Bean properties
* @param visitor the visitor to raise events when Java Bean properties are found
*/
void inspect(final Object instance, final BeanVisitor visitor) {
inspect(instance, new CamelCaseNamingStrategy(), visitor);
}
/**
* Inspect the supplied object and fire callbacks on the supplied {@link BeanVisitor} for every property exposed on the object
*
* @param instance an object instance to inspect for Java Bean properties
* @param naming the naming strategy to use for the Java Bean properties
* @param visitor the visitor to raise events when Java Bean properties are found
*/
void inspect(final Object instance, final BeanNamingStrategy naming, final BeanVisitor visitor) {
try {
if (instance != null) {
inspectObject(new ArrayList(), new BeanPropertyPath(naming.describeRoot(instance.getClass())), naming, instance, visitor);
}
} finally {
inspected.get().clear();
}
}
@SuppressWarnings("rawtypes")
private void inspectObject(final List currentStack, final BeanPropertyPath path, final BeanNamingStrategy naming, final Object instance, final BeanVisitor visitor) {
if (instance == null) {
return;
}
final List stack = new ArrayList(currentStack);
logInspection(path, "Object", instance);
if (isDenyOverflow()) {
int instanceKey = identityHashCode(instance);
Integer hits = inspected.get().get(instanceKey);
if (hits != null) {
if (hits > OBJECT_HITS_BEFORE_OVERFLOW) {
return;
} else {
inspected.get().put(instanceKey, ++hits);
}
} else {
inspected.get().put(instanceKey, 1);
}
}
if (!isInspectChildren() && currentStack.size() > 0) {
return;
}
Type type = type(instance.getClass());
if (type.isArray()) {
inspectArray(new ArrayList(), path, naming, instance, visitor);
} else if (type.is(Iterable.class)) {
inspectIterable(new ArrayList(), path, naming, (Iterable) instance, visitor);
} else if (type.is(Map.class)) {
inspectMap(new ArrayList(), path, naming, (Map) instance, visitor);
} else {
BeanPropertyPath rootPath = path.isEmpty() ? new BeanPropertyPath(naming.describeType(instance.getClass())) : path;
stack.add(instance);
for (TypeProperty property : type.setNamingStrategy(naming).propertyList()) {
BeanPropertyPath nextPath = rootPath.append(property.getName());
visitor.visit(new BeanProperty(property.getName(), property.getAccessor(), property.getMutator(), instance), instance, nextPath, stack.toArray());
if (property.isArray()) {
Object value = property.getValue(instance);
if (value != null) {
inspectArray(stack, nextPath, naming, value, visitor);
}
} else if (property.isIterable()) {
Iterable value = property.getValue(instance, Iterable.class);
if (value != null) {
inspectIterable(stack, nextPath, naming, value, visitor);
}
} else if (property.isMap()) {
Map value = property.getValue(instance, Map.class);
if (value != null) {
inspectMap(stack, nextPath, naming, value, visitor);
}
} else {
try {
Object propertyValue = property.getValue(instance);
if (propertyValue != null) {
inspectObject(stack, nextPath, naming, propertyValue, visitor);
}
} catch (Exception e) {
LOG.trace("Skip {}. Exception thrown on calling get", property);
}
}
}
}
}
private boolean isDenyOverflow() {
return DENY_OVERFLOW.equals(overflow);
}
private boolean isInspectChildren() {
return DEEP.equals(depth);
}
private void inspectMap(final List stack, final BeanPropertyPath path, final BeanNamingStrategy naming, final Map, ?> instance, final BeanVisitor visitor) {
logInspection(path, "Map", instance);
for (Map.Entry, ?> entry : instance.entrySet()) {
BeanPropertyPath nextPath = path.isEmpty() ? new BeanPropertyPath(naming.describeType(Map.class)) : path;
inspectObject(stack, nextPath.appendIndex(entry.getKey().toString()), naming, entry.getValue(), visitor);
}
}
private void inspectArray(final List stack, final BeanPropertyPath path, final BeanNamingStrategy naming, final Object instance, final BeanVisitor visitor) {
logInspection(path, "Array", instance);
for (int i = 0; i < Array.getLength(instance); ++i) {
BeanPropertyPath nextPath = path.isEmpty() ? new BeanPropertyPath(naming.describeType(Array.class)) : path;
inspectObject(stack, nextPath.appendIndex(i), naming, Array.get(instance, i), visitor);
}
}
private void inspectIterable(final List stack, final BeanPropertyPath path, final BeanNamingStrategy naming, final Iterable> instance, final BeanVisitor visitor) {
logInspection(path, "Iterable", instance);
int seq = 0;
for (Object object : instance) {
BeanPropertyPath nextPath = path.isEmpty() ? new BeanPropertyPath(naming.describeType(Collection.class)) : path;
inspectObject(stack, nextPath.appendIndex(seq++), naming, object, visitor);
}
}
private void logInspection(final BeanPropertyPath path, final String loggedType, final Object instance) {
LOG.trace("Inspect Path [{}]. {} [{}:{}]", new Object[] {
path.fullPath(), loggedType, instance.getClass().getSimpleName(), identityHashCode(instance)
});
}
}