2 * Copyright 2005-2010 the original author or authors.
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
8 * http://www.apache.org/licenses/LICENSE-2.0
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
16 package org.wamblee.reflection;
18 import java.lang.reflect.Array;
19 import java.lang.reflect.Field;
20 import java.lang.reflect.InvocationTargetException;
21 import java.lang.reflect.Method;
22 import java.lang.reflect.Modifier;
23 import java.util.ArrayList;
24 import java.util.List;
27 import java.util.Map.Entry;
29 import org.wamblee.general.ObjectElem;
33 * Class encapsulating object traversal through the fields and properties of an
34 * object. The class accepts a visitor in its constructor whose job it is to
35 * process any visited fields of the object.
39 * The following fields and methods are excluded:
42 * <li>All fields and methods of the {@link Object} class.</li>
43 * <li>All fields and methods of collecation classes (List, Map, Set), and of
47 * @author Erik Brakkee
49 public class ObjectTraversal {
52 * Visitor interface to be implemented for object traversal.
54 * @author Erik Brakkee
57 public static interface ObjectVisitor {
59 * Determines if the given class must be visited.
63 * @return True when visited, false otherwise.
65 boolean mustVisit(Class aClass);
68 * Determines if a given field must be visited. By default all declared
69 * fields (including private) are visited.
72 * @return True when visited.
74 boolean mustVisit(Field aField);
77 * Determines if the given property accessor must be visited.
81 * @return True when visited.
83 boolean mustVisit(Method aMethod);
90 * @return True if the object's fields and methods must be visited.
92 boolean visitPlainObject(Object aObject);
99 * @return True if the collection's elements must be visited as well.
101 boolean visitList(List aObject);
108 * @return True if the map's values must be visited as well.
110 boolean visitMap(Map aObject);
117 * @return True if the collection's elements must be visited as well.
119 boolean visitSet(Set aSet);
126 * @return True if the array's elements must be visited as well.
128 boolean visitArray(Object aArray);
131 private ObjectVisitor visitor;
132 private List<ObjectElem> excluded;
135 * Constructs the traversal.
140 public ObjectTraversal(ObjectVisitor aVisitor) {
142 excluded = new ArrayList<ObjectElem>();
146 * Adds an object instance to exclude from traversal.
151 public void addExcludedObject(Object aObject) {
152 excluded.add(new ObjectElem(aObject));
155 public void accept(Object aObject) {
156 if (aObject == null) {
159 if (aObject.getClass().equals(Object.class)) {
162 if (ReflectionUtils.isPrimitive(aObject.getClass())) {
165 if (!visitor.mustVisit(aObject.getClass())) {
169 if (alreadyProcessed(aObject)) {
173 if (aObject instanceof List) {
174 if (visitor.visitList((List) aObject)) {
175 processList((List) aObject);
178 } else if (aObject instanceof Map) {
179 if (visitor.visitMap((Map) aObject)) {
180 processMap((Map) aObject);
183 } else if (aObject instanceof Set) {
184 if (visitor.visitSet((Set) aObject)) {
185 processSet((Set) aObject);
188 } else if (aObject.getClass().isArray()) {
189 if (visitor.visitArray(aObject)) {
190 processArray(aObject);
194 if (!visitor.visitPlainObject(aObject)) {
199 List<Method> methods = ReflectionUtils.getAllMethods(
200 aObject.getClass(), Object.class);
202 for (Method getter : methods) {
203 if ((getter.getName().startsWith("get") || getter.getName()
204 .startsWith("is")) &&
205 !Modifier.isStatic(getter.getModifiers()) &&
206 getter.getParameterTypes().length == 0 &&
207 getter.getReturnType() != Void.class) {
209 if (visitor.mustVisit(getter)) {
210 acceptMethod(aObject, getter);
215 List<Field> fields = ReflectionUtils.getAllFields(aObject.getClass(),
217 for (Field field : fields) {
218 int modifiers = field.getModifiers();
219 if (!Modifier.isStatic(modifiers) && !Modifier.isFinal(modifiers)) {
220 field.setAccessible(true);
221 if (visitor.mustVisit(field)) {
222 acceptField(aObject, field);
228 private void acceptMethod(Object aObject, Method aGetter) {
230 Object value = aGetter.invoke(aObject);
235 } catch (InvocationTargetException e) {
236 throw new RuntimeException(e.getMessage(), e);
237 } catch (IllegalAccessException e) {
238 throw new RuntimeException(e.getMessage(), e);
242 private void acceptField(Object aObject, Field aField) {
244 Object value = aField.get(aObject);
249 } catch (IllegalAccessException e) {
250 throw new RuntimeException(e.getMessage(), e);
254 private void acceptPlainOrCollection(Object value) {
255 if (value instanceof Set) {
256 visitor.visitSet((Set) value);
257 processSet((Set) value);
258 } else if (value instanceof List) {
259 processList((List) value);
260 } else if (value instanceof Map) {
261 processMap((Map<?, ?>) value);
262 } else if (value.getClass().isArray()) {
269 private boolean alreadyProcessed(Object aObject) {
270 ObjectElem elem = new ObjectElem(aObject);
271 if (excluded.contains(elem)) {
278 private void processList(List aObject) {
279 for (Object obj : aObject) {
284 private void processSet(Set aObject) {
285 for (Object obj : aObject) {
290 public <Key, Value> void processMap(Map<Key, Value> aMap) {
291 Set<Entry<Key, Value>> entries = aMap.entrySet();
293 for (Entry<Key, Value> entry : entries) {
294 Value value = entry.getValue();
299 public void processArray(Object aObject) {
300 int size = Array.getLength(aObject);
301 for (int i = 0; i < size; i++) {
302 accept(Array.get(aObject, i));