(no commit message)
authorErik Brakkee <erik@brakkee.org>
Sat, 31 Jul 2010 12:54:09 +0000 (12:54 +0000)
committerErik Brakkee <erik@brakkee.org>
Sat, 31 Jul 2010 12:54:09 +0000 (12:54 +0000)
support/general/src/main/java/org/wamblee/reflection/ObjectTraversal.java [new file with mode: 0644]
support/general/src/main/java/org/wamblee/reflection/ReflectionUtils.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 (file)
index 0000000..d5955fc
--- /dev/null
@@ -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;
+
+/**
+ * <p>
+ * 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.
+ * </p>
+ * 
+ * <p>
+ * The following fields and methods are excluded:
+ * </p>
+ * <ul>
+ * <li>All fields and methods of the {@link Object} class.</li>
+ * <li>All fields and methods of collecation classes (List, Map, Set), and of
+ * arrays.</li>
+ * </ul>
+ * 
+ * @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<ObjectElem> excluded;
+
+    /**
+     * Constructs the traversal.
+     * 
+     * @param aVisitor
+     *            Visitor to use.
+     */
+    public ObjectTraversal(ObjectVisitor aVisitor) {
+        visitor = aVisitor;
+        excluded = new ArrayList<ObjectElem>();
+    }
+
+    /**
+     * 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<Method> 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<Field> 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 <Key, Value> void processMap(Map<Key, Value> aMap) {
+        Set<Entry<Key, Value>> entries = aMap.entrySet();
+
+        for (Entry<Key, Value> 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));
+        }
+    }
+
+}
index 7c5c45f94fd7f1e2d44a58fafbf38b7cd3f444f2..0da2d1f2ff31fb93e3518a031686b30aff34b239 100644 (file)
@@ -29,6 +29,20 @@ import java.util.Map;
  * @author Erik Brakkee
  */
 public class ReflectionUtils {
+    
+    public static final List<Class> PRIMITIVE_WRAPPERS =
+        createPrimitiveWrappers();
+    
+    private static final List<Class> 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);
+    }
 }