001
014
015 package com.liferay.portal.kernel.util;
016
017 import java.lang.annotation.Annotation;
018 import java.lang.reflect.Array;
019 import java.lang.reflect.Field;
020 import java.lang.reflect.Modifier;
021
022 import java.util.Collections;
023 import java.util.IdentityHashMap;
024 import java.util.LinkedList;
025 import java.util.Queue;
026 import java.util.Set;
027
028
031 public class ObjectGraphUtil {
032
033 public static void walkObjectGraph(Object object, Visitor visitor) {
034 Queue<Object> queue = new LinkedList<>();
035
036 queue.offer(object);
037
038 Set<Object> visitedObjects = Collections.newSetFromMap(
039 new IdentityHashMap<Object, Boolean>());
040
041 while ((object = queue.poll()) != null) {
042 if (!visitedObjects.add(object)) {
043 continue;
044 }
045
046 Class<?> clazz = object.getClass();
047
048 if (clazz.isArray()) {
049 clazz = clazz.getComponentType();
050
051 if (clazz.isPrimitive()) {
052 continue;
053 }
054
055 for (int i = 0; i < Array.getLength(object); i++) {
056 Object element = Array.get(object, i);
057
058 if (element != null) {
059 queue.offer(element);
060 }
061 }
062
063 continue;
064 }
065
066 while (clazz != null) {
067 for (Field field : clazz.getDeclaredFields()) {
068 if (Modifier.isStatic(field.getModifiers())) {
069 continue;
070 }
071
072 field.setAccessible(true);
073
074 try {
075 Object value = visitor.visit(field, object);
076
077 Class<?> type = field.getType();
078
079 if ((value != null) && !type.isPrimitive()) {
080 queue.offer(value);
081 }
082 }
083 catch (Exception e) {
084 ReflectionUtil.throwException(e);
085 }
086 }
087
088 clazz = clazz.getSuperclass();
089 }
090 }
091 }
092
093 public static abstract class AnnotatedFieldMappingVisitor
094 implements Visitor {
095
096 public AnnotatedFieldMappingVisitor(
097 Set<Class<?>> linkedClasses,
098 Set<Class<? extends Annotation>> annotationClasses,
099 Set<Class<?>> fieldTypeClasses) {
100
101 _linkedClasses = linkedClasses;
102 _annotationClasses = annotationClasses;
103 _fieldTypeClasses = fieldTypeClasses;
104 }
105
106 @Override
107 public Object visit(Field field, Object target) throws Exception {
108 Object value = field.get(target);
109
110 if ((value == null) || !isLinkedClass(field.getDeclaringClass())) {
111 return null;
112 }
113
114 if (!hasAnnotation(field.getAnnotations()) ||
115 !isFieldTypeClass(field.getType())) {
116
117 return value;
118 }
119
120 field = ReflectionUtil.unfinalField(field);
121
122 field.set(target, mapValue(field, value));
123
124 return null;
125 }
126
127 protected abstract Object doMap(Field field, Object value);
128
129 protected boolean hasAnnotation(Annotation[] annotations) {
130 for (Annotation annotation : annotations) {
131 for (Class<? extends Annotation> annotationClass :
132 _annotationClasses) {
133
134 if (annotationClass.isInstance(annotation)) {
135 return true;
136 }
137 }
138 }
139
140 return false;
141 }
142
143 protected boolean isFieldTypeClass(Class<?> clazz) {
144 Class<?> componentType = clazz;
145 Class<?> currentComponentType = clazz.getComponentType();
146
147 while (currentComponentType != null) {
148 componentType = currentComponentType;
149
150 currentComponentType = currentComponentType.getComponentType();
151 }
152
153 for (Class<?> fieldTypeClass : _fieldTypeClasses) {
154 if (fieldTypeClass.isAssignableFrom(componentType)) {
155 return true;
156 }
157 }
158
159 return false;
160 }
161
162 protected boolean isLinkedClass(Class<?> clazz) {
163 for (Class<?> linkedClass : _linkedClasses) {
164 if (linkedClass.isAssignableFrom(clazz)) {
165 return true;
166 }
167 }
168
169 return false;
170 }
171
172 protected Object mapValue(Field field, Object value) {
173 for (Class<?> fieldTypeClass : _fieldTypeClasses) {
174 if (fieldTypeClass.isInstance(value)) {
175 return doMap(field, value);
176 }
177 }
178
179 value = ReflectionUtil.arrayClone(value);
180
181 for (int i = 0; i < Array.getLength(value); i++) {
182 Array.set(value, i, mapValue(field, Array.get(value, i)));
183 }
184
185 return value;
186 }
187
188 private final Set<Class<? extends Annotation>> _annotationClasses;
189 private final Set<Class<?>> _fieldTypeClasses;
190 private final Set<Class<?>> _linkedClasses;
191
192 }
193
194 public interface Visitor {
195
196 public Object visit(Field field, Object target) throws Exception;
197
198 }
199
200 }