001: /*
002: * Copyright 2006-2007, Unitils.org
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.unitils.reflectionassert;
017:
018: import java.lang.reflect.AccessibleObject;
019: import java.lang.reflect.Field;
020: import java.lang.reflect.Modifier;
021: import java.util.Map;
022: import java.util.Stack;
023:
024: /**
025: * todo javadoc
026: *
027: * @author Tim Ducheyne
028: * @author Filip Neven
029: */
030: public class ObjectComparator extends ReflectionComparator {
031:
032: // todo javadoc
033: public ObjectComparator(ReflectionComparator chainedComparator) {
034: super (chainedComparator);
035: }
036:
037: // todo javadoc
038: public boolean canHandle(Object left, Object right) {
039: return left != null && right != null;
040: }
041:
042: // todo javadoc
043: @Override
044: protected Difference doGetDifference(Object left, Object right,
045: Stack<String> fieldStack,
046: Map<TraversedInstancePair, Boolean> traversedInstancePairs) {
047: // check different class type
048: Class<?> clazz = left.getClass();
049: if (!clazz.equals(right.getClass())) {
050: return new Difference("Different class types. Left: "
051: + clazz + ", right: " + right.getClass(), left,
052: right, fieldStack);
053: }
054: // compare all fields of the object using reflection
055: return compareFields(left, right, clazz, fieldStack,
056: traversedInstancePairs);
057: }
058:
059: /**
060: * Compares the values of all fields in the given objects by use of reflection.
061: *
062: * @param left the left object for the comparison, not null
063: * @param right the right object for the comparison, not null
064: * @param clazz the type of both objects
065: * @param fieldStack the current field names
066: * @param traversedInstancePairs Map with pairs of objects that have been compared with each other.
067: * @return the difference, null if there is no difference
068: */
069: protected Difference compareFields(Object left, Object right,
070: Class<?> clazz, Stack<String> fieldStack,
071: Map<TraversedInstancePair, Boolean> traversedInstancePairs) {
072: Field[] fields = clazz.getDeclaredFields();
073: AccessibleObject.setAccessible(fields, true);
074:
075: for (Field f : fields) {
076: fieldStack.push(f.getName());
077:
078: // skip transient and static fields
079: if (Modifier.isTransient(f.getModifiers())
080: || Modifier.isStatic(f.getModifiers())) {
081: fieldStack.pop();
082: continue;
083: }
084: try {
085:
086: // recursively check the value of the fields
087: Difference difference = rootComparator.getDifference(f
088: .get(left), f.get(right), fieldStack,
089: traversedInstancePairs);
090: if (difference != null) {
091: return difference;
092: }
093:
094: } catch (IllegalAccessException e) {
095: // this can't happen. Would get a Security exception instead
096: // throw a runtime exception in case the impossible happens.
097: throw new InternalError(
098: "Unexpected IllegalAccessException");
099: }
100: fieldStack.pop();
101: }
102:
103: // compare fields declared in superclass
104: Class<?> super clazz = clazz.getSuperclass();
105: while (super clazz != null
106: && !super clazz.getName().startsWith("java.lang")) {
107: Difference difference = compareFields(left, right,
108: superclazz, fieldStack, traversedInstancePairs);
109: if (difference != null) {
110: return difference;
111: }
112: superclazz = superclazz.getSuperclass();
113: }
114: return null;
115: }
116: }
|