/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.portal.kernel.util; import java.lang.annotation.Annotation; import java.lang.reflect.Array; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.Collections; import java.util.IdentityHashMap; import java.util.LinkedList; import java.util.Queue; import java.util.Set; /** * @author Shuyang Zhou */ public class ObjectGraphUtil { public static void walkObjectGraph(Object object, Visitor visitor) { Queue<Object> queue = new LinkedList<>(); queue.offer(object); Set<Object> visitedObjects = Collections.newSetFromMap( new IdentityHashMap<Object, Boolean>()); while ((object = queue.poll()) != null) { if (!visitedObjects.add(object)) { continue; } Class<?> clazz = object.getClass(); if (clazz.isArray()) { clazz = clazz.getComponentType(); if (clazz.isPrimitive()) { continue; } for (int i = 0; i < Array.getLength(object); i++) { Object element = Array.get(object, i); if (element != null) { queue.offer(element); } } continue; } while (clazz != null) { for (Field field : clazz.getDeclaredFields()) { if (Modifier.isStatic(field.getModifiers())) { continue; } field.setAccessible(true); try { Object value = visitor.visit(field, object); Class<?> type = field.getType(); if ((value != null) && !type.isPrimitive()) { queue.offer(value); } } catch (Exception e) { ReflectionUtil.throwException(e); } } clazz = clazz.getSuperclass(); } } } public abstract static class AnnotatedFieldMappingVisitor implements Visitor { public AnnotatedFieldMappingVisitor( Set<Class<?>> linkedClasses, Set<Class<? extends Annotation>> annotationClasses, Set<Class<?>> fieldTypeClasses) { _linkedClasses = linkedClasses; _annotationClasses = annotationClasses; _fieldTypeClasses = fieldTypeClasses; } @Override public Object visit(Field field, Object target) throws Exception { Object value = field.get(target); if ((value == null) || !isLinkedClass(field.getDeclaringClass())) { return null; } if (!hasAnnotation(field.getAnnotations()) || !isFieldTypeClass(field.getType())) { return value; } field = ReflectionUtil.unfinalField(field); field.set(target, mapValue(field, value)); return null; } protected abstract Object doMap(Field field, Object value); protected boolean hasAnnotation(Annotation[] annotations) { for (Annotation annotation : annotations) { for (Class<? extends Annotation> annotationClass : _annotationClasses) { if (annotationClass.isInstance(annotation)) { return true; } } } return false; } protected boolean isFieldTypeClass(Class<?> clazz) { Class<?> componentType = clazz; Class<?> currentComponentType = clazz.getComponentType(); while (currentComponentType != null) { componentType = currentComponentType; currentComponentType = currentComponentType.getComponentType(); } for (Class<?> fieldTypeClass : _fieldTypeClasses) { if (fieldTypeClass.isAssignableFrom(componentType)) { return true; } } return false; } protected boolean isLinkedClass(Class<?> clazz) { for (Class<?> linkedClass : _linkedClasses) { if (linkedClass.isAssignableFrom(clazz)) { return true; } } return false; } protected Object mapValue(Field field, Object value) { for (Class<?> fieldTypeClass : _fieldTypeClasses) { if (fieldTypeClass.isInstance(value)) { return doMap(field, value); } } value = ReflectionUtil.arrayClone(value); for (int i = 0; i < Array.getLength(value); i++) { Array.set(value, i, mapValue(field, Array.get(value, i))); } return value; } private final Set<Class<? extends Annotation>> _annotationClasses; private final Set<Class<?>> _fieldTypeClasses; private final Set<Class<?>> _linkedClasses; } public interface Visitor { public Object visit(Field field, Object target) throws Exception; } }