From 313c76d2f2ef7da225beaabbb7be8715d018ab4d Mon Sep 17 00:00:00 2001 From: Erik Brakkee Date: Sat, 31 Jul 2010 12:54:09 +0000 Subject: [PATCH] --- .../wamblee/reflection/ObjectTraversal.java | 306 ++++++++++++++++++ .../wamblee/reflection/ReflectionUtils.java | 26 ++ 2 files changed, 332 insertions(+) create mode 100644 support/general/src/main/java/org/wamblee/reflection/ObjectTraversal.java diff --git a/support/general/src/main/java/org/wamblee/reflection/ObjectTraversal.java b/support/general/src/main/java/org/wamblee/reflection/ObjectTraversal.java new file mode 100644 index 00000000..d5955fc3 --- /dev/null +++ b/support/general/src/main/java/org/wamblee/reflection/ObjectTraversal.java @@ -0,0 +1,306 @@ +/* + * 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 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: + *

+ * + * + * @author Erik Brakkee + */ +public class ObjectTraversal { + + /** + * 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 ObjectVisitor visitor; + private List excluded; + + /** + * Constructs the traversal. + * + * @param aVisitor + * Visitor to use. + */ + public ObjectTraversal(ObjectVisitor aVisitor) { + 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)); + } + + 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 (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)) { + 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)) { + 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 value) { + if (value instanceof Set) { + visitor.visitSet((Set) value); + processSet((Set) value); + } else if (value instanceof List) { + processList((List) value); + } else if (value instanceof Map) { + processMap((Map) value); + } else if (value.getClass().isArray()) { + processArray(value); + } else { + accept(value); + } + } + + 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)); + } + } + +} diff --git a/support/general/src/main/java/org/wamblee/reflection/ReflectionUtils.java b/support/general/src/main/java/org/wamblee/reflection/ReflectionUtils.java index 7c5c45f9..0da2d1f2 100644 --- a/support/general/src/main/java/org/wamblee/reflection/ReflectionUtils.java +++ b/support/general/src/main/java/org/wamblee/reflection/ReflectionUtils.java @@ -29,6 +29,20 @@ import java.util.Map; * @author Erik Brakkee */ public class ReflectionUtils { + + public static final List PRIMITIVE_WRAPPERS = + createPrimitiveWrappers(); + + private static final List createPrimitiveWrappers() { + Class[] vals = { + Boolean.class, Byte.class, Character.class, Short.class, Integer.class, Long.class, + Float.class, Double.class }; + return Arrays.asList(vals); + } + + + + /** * Wraps a type by the corresponding wrapper type if it is a primitive type. * @@ -141,4 +155,16 @@ public class ReflectionUtils { getAllFields(superClass, aFound, aExcludedClasses); } } + + /** + * Checks if a class is a primitive type or wrapper type. + * @param aClass + * @return + */ + public static boolean isPrimitive(Class aClass) { + if ( aClass.isPrimitive()) { + return true; + } + return PRIMITIVE_WRAPPERS.contains(aClass); + } } -- 2.31.1