001    /**
002     * Copyright (c) 2000-present Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
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    /**
029     * @author Shuyang Zhou
030     */
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    }