org.wamblee.reflection.ObjectTraversal Maven / Gradle / Ivy
The newest version!
/*
* Copyright 2005-2010 the original author or authors.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.wamblee.reflection;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.wamblee.general.ObjectElem;
/**
*
* Class encapsulating object traversal through the fields and properties of an
* object. The class accepts a visitor in its constructor whose job it is to
* process any visited fields of the object.
*
*
*
* The following fields and methods are excluded:
*
*
* - All fields and methods of the {@link Object} class.
* - All fields and methods of collection classes (List, Map, Set), and of
* arrays.
*
*
* @author Erik Brakkee
*/
public class ObjectTraversal {
public static final Logger LOGGER = Logger.getLogger(ObjectTraversal.class
.getName());
/**
* Visitor interface to be implemented for object traversal.
*
* @author Erik Brakkee
*
*/
public static interface ObjectVisitor {
/**
* Determines if the given class must be visited.
*
* @param aClass
* Class.
* @return True when visited, false otherwise.
*/
boolean mustVisit(Class aClass);
/**
* Determines if a given field must be visited. By default all declared
* fields (including private) are visited.
*
* @param aField
* @return True when visited.
*/
boolean mustVisit(Field aField);
/**
* Determines if the given property accessor must be visited.
*
* @param aMethod
* Method to visit.
* @return True when visited.
*/
boolean mustVisit(Method aMethod);
/**
* Visit an object.
*
* @param aObject
* Object to process
* @return True if the object's fields and methods must be visited.
*/
boolean visitPlainObject(Object aObject);
/**
* Visit a collection
*
* @param aObject
* Object to process.
* @return True if the collection's elements must be visited as well.
*/
boolean visitList(List aObject);
/**
* Visit a collection
*
* @param aObject
* Object to process.
* @return True if the map's values must be visited as well.
*/
boolean visitMap(Map aObject);
/**
* Visit a collection
*
* @param aObject
* Object to process.
* @return True if the collection's elements must be visited as well.
*/
boolean visitSet(Set aSet);
/**
* Visit a collection
*
* @param aObject
* Object to process.
* @return True if the array's elements must be visited as well.
*/
boolean visitArray(Object aArray);
}
private int level;
private ObjectVisitor visitor;
private List excluded;
/**
* Constructs the traversal.
*
* @param aVisitor
* Visitor to use.
*/
public ObjectTraversal(ObjectVisitor aVisitor) {
level = 0;
visitor = aVisitor;
excluded = new ArrayList();
}
/**
* Adds an object instance to exclude from traversal.
*
* @param aObject
* Object to add.
*/
public void addExcludedObject(Object aObject) {
excluded.add(new ObjectElem(aObject));
}
private String indent() {
StringBuffer buf = new StringBuffer();
for (int i = 1; i < level; i++) {
buf.append(" ");
}
return buf.toString();
}
public void accept(Object aObject) {
if (aObject == null) {
return;
}
if (aObject.getClass().equals(Object.class)) {
return;
}
if (ReflectionUtils.isPrimitive(aObject.getClass())) {
return;
}
if (!visitor.mustVisit(aObject.getClass())) {
return;
}
if (alreadyProcessed(aObject)) {
return;
}
if (LOGGER.isLoggable(Level.FINEST)) {
level++;
LOGGER.finest(indent() + "obj: " + aObject);
}
if (aObject instanceof List) {
if (visitor.visitList((List) aObject)) {
processList((List) aObject);
}
return;
} else if (aObject instanceof Map) {
if (visitor.visitMap((Map) aObject)) {
processMap((Map) aObject);
}
return;
} else if (aObject instanceof Set) {
if (visitor.visitSet((Set) aObject)) {
processSet((Set) aObject);
}
return;
} else if (aObject.getClass().isArray()) {
if (visitor.visitArray(aObject)) {
processArray(aObject);
}
return;
} else {
if (!visitor.visitPlainObject(aObject)) {
return;
}
}
List methods = ReflectionUtils.getAllMethods(
aObject.getClass(), Object.class);
for (Method getter : methods) {
if ((getter.getName().startsWith("get") || getter.getName()
.startsWith("is")) &&
!Modifier.isStatic(getter.getModifiers()) &&
getter.getParameterTypes().length == 0 &&
getter.getReturnType() != Void.class) {
if (visitor.mustVisit(getter)) {
if (LOGGER.isLoggable(Level.FINEST)) {
LOGGER
.finest(indent() + "method: " + getter.getName());
}
acceptMethod(aObject, getter);
}
}
}
List fields = ReflectionUtils.getAllFields(aObject.getClass(),
Object.class);
for (Field field : fields) {
int modifiers = field.getModifiers();
if (!Modifier.isStatic(modifiers) && !Modifier.isFinal(modifiers)) {
field.setAccessible(true);
if (visitor.mustVisit(field)) {
if (LOGGER.isLoggable(Level.FINEST)) {
LOGGER.finest(indent() + "field: " + field.getName());
}
acceptField(aObject, field);
}
}
}
}
private void acceptMethod(Object aObject, Method aGetter) {
try {
Object value = aGetter.invoke(aObject);
if (value == null) {
return;
}
accept(value);
} catch (InvocationTargetException e) {
throw new RuntimeException(e.getMessage(), e);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
private void acceptField(Object aObject, Field aField) {
try {
Object value = aField.get(aObject);
if (value == null) {
return;
}
accept(value);
} catch (IllegalAccessException e) {
throw new RuntimeException(e.getMessage(), e);
}
}
private void acceptPlainOrCollection(Object aValue) {
if (aValue instanceof Set) {
visitor.visitSet((Set) aValue);
processSet((Set) aValue);
} else if (aValue instanceof List) {
processList((List) aValue);
} else if (aValue instanceof Map) {
processMap((Map, ?>) aValue);
} else if (aValue.getClass().isArray()) {
processArray(aValue);
} else {
accept(aValue);
}
}
private boolean alreadyProcessed(Object aObject) {
ObjectElem elem = new ObjectElem(aObject);
if (excluded.contains(elem)) {
return true;
}
excluded.add(elem);
return false;
}
private void processList(List aObject) {
for (Object obj : aObject) {
accept(obj);
}
}
private void processSet(Set aObject) {
for (Object obj : aObject) {
accept(obj);
}
}
public void processMap(Map aMap) {
Set> entries = aMap.entrySet();
for (Entry entry : entries) {
Value value = entry.getValue();
accept(value);
}
}
public void processArray(Object aObject) {
int size = Array.getLength(aObject);
for (int i = 0; i < size; i++) {
accept(Array.get(aObject, i));
}
}
}