/* * Copyright 2006 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. * * Modified by Clam <clamisgood@gmail.com> * * This file is part of LateralGM. * LateralGM is free software and comes with ABSOLUTELY NO WARRANTY. * See LICENSE for details. */ package org.lateralgm.compare; import java.lang.reflect.AccessibleObject; import java.lang.reflect.Field; import java.lang.reflect.Modifier; import java.util.HashMap; import java.util.Set; import java.util.Stack; /** * @author Tim Ducheyne * @author Filip Neven */ public class ObjectComparator extends ReflectionComparator { public ObjectComparator(ReflectionComparator chainedComparator) { super(chainedComparator); } public boolean canHandle(Object left, Object right) { return left != null && right != null; } protected Difference doGetDifference(Object left, Object right, Stack<String> fieldStack, Set<TraversedInstancePair> traversedInstancePairs) { // check different class type Class<?> clazz = left.getClass(); if (!clazz.equals(right.getClass())) { return new Difference("Different class types. Left: " + clazz + ", right: " + right.getClass(),left,right,fieldStack); } // If an equals method is implemented, use it if (hasEqualsImpl(clazz)) return left.equals(right) ? null : new Difference("inequality by use of equals method",left, right,fieldStack); // compare all fields of the object using reflection return compareFields(left,right,clazz,fieldStack,traversedInstancePairs); } /** * Compares the values of all fields in the given objects by use of reflection. * * @param left the left object for the comparison, not null * @param right the right object for the comparison, not null * @param clazz the type of both objects * @param fieldStack the current field names * @param traversedInstancePairs * @return the difference, null if there is no difference */ protected Difference compareFields(Object left, Object right, Class<?> clazz, Stack<String> fieldStack, Set<TraversedInstancePair> traversedInstancePairs) { Field[] fields = clazz.getDeclaredFields(); AccessibleObject.setAccessible(fields,true); traversedInstancePairs.add(new TraversedInstancePair(left,right)); String[] excludes = exclusions.get(clazz); for (Field f : fields) { fieldStack.push(f.getName()); // skip transient, static and excluded fields if (Modifier.isTransient(f.getModifiers()) || Modifier.isStatic(f.getModifiers()) || contains(excludes,f.getName())) { fieldStack.pop(); continue; } try { // recursively check the value of the fields Difference difference = rootComparator.getDifference(f.get(left),f.get(right),fieldStack, traversedInstancePairs); if (difference != null) { return difference; } } catch (IllegalAccessException e) { // this can't happen. Would get a Security exception instead // throw a runtime exception in case the impossible happens. throw new InternalError("Unexpected IllegalAccessException"); } catch (ClassCastException e) { System.out.println(f); throw e; } fieldStack.pop(); } // compare fields declared in superclass Class<?> superclazz = clazz.getSuperclass(); while (superclazz != null && !superclazz.getName().startsWith("java.lang")) { Difference dif = compareFields(left,right,superclazz,fieldStack,traversedInstancePairs); if (dif != null) { return dif; } superclazz = superclazz.getSuperclass(); } return null; } private static boolean hasEqualsImpl(Class<?> clazz) { try { return clazz.getMethod("equals",Object.class).getDeclaringClass() != Object.class; } catch (NoSuchMethodException e) { e.printStackTrace(); return false; } } public static boolean contains(Object[] dat, Object target) { if (dat == null) return false; for (Object o : dat) if (target.equals(o)) return true; return false; } protected HashMap<Class<?>,String[]> exclusions = new HashMap<Class<?>,String[]>(); public void addExclusions(Class<?> clazz, String...fieldNames) { exclusions.put(clazz,fieldNames); } }