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 org.unitils.core.UnitilsException;
019: import static org.unitils.reflectionassert.ReflectionComparatorMode.*;
020: import static org.unitils.util.CollectionUtils.asSet;
021:
022: import java.util.HashMap;
023: import java.util.HashSet;
024: import java.util.Map;
025: import java.util.Set;
026:
027: /**
028: * This class functions as a factory for reflection comparator chains. A reflection comparator chain is a chain of
029: * instances of {@link ReflectionComparator} subclasses. A reflection comparator chain will compare two objects with
030: * each other using reflection, and returns a {@link ReflectionComparator.Difference} object. You can use a reflection
031: * comparator chain by invoking {@link ReflectionComparator#getDifference(Object,Object)} on the root of the chain.
032: * <p/>
033: * The {@link Object#equals} method is often used for business logic equality checking. The {@link Object#equals} method
034: * can for example return true when the id fields of 2 instances have equal values, no matter what the values of the
035: * other fields are. A reflection comparator chain offers another way to check equality of objects.
036: * <p/>
037: * A reflection comparator chain will use reflection to get and compare the values of all fields in
038: * the objects. If field contains another object, the same reflection comparison will be done recursively on these inner
039: * objects. All fields in superclasses will also be compared using reflection. Static and transient fields will be ignored.
040: * <p/>
041: * As an exception, the {@link Object#equals} method will be called instead of using reflection on all
042: * java.lang.* type field values. Eg a field of type java.lang.Integer will be compared using its equals method. No
043: * superclass comparison is done on java.lang.* type classes. Eg the java.lang.Object class fields will not be compared.
044: * <p/>
045: * If an object is an array or a collection, all its elements will be traversed and compared with the other array or
046: * collection in the same way using reflection. The actual type of collection or whether a collection is compared with
047: * an array is not important. It will only go through the array or collection and compare the elements. For example, an
048: * Arraylist can be compared with an array or a LinkedList.
049: * <p/>
050: * By default, a strict comparison is performed, but if needed, some leniency can be configured by setting one or more
051: * comparator modes: <ul>
052: * <li>ignore defaults: all fields that have a default java value for the left object will be ignored. Eg if
053: * the left object contains an int field with value 0 it will not be compared to the value of the right object.</li>
054: * <li>lenient dates: only check whether both Date objects contain a value or not, the value itself
055: * is not compared. Eg. if the left object contained a date with value 1-1-2006 and the right object contained a date
056: * with value 2-2-2006 they would still be considered equal.</li>
057: * <li>lenient order: only check whether both collections or arrays contain the same value, the actual order of the
058: * values is not compared. Eg. if the left object is int[]{ 1, 2} and the right value is int[]{2, 1} they would still
059: * be considered equal.</li>
060: * </ul>
061: *
062: * @author Filip Neven
063: * @author Tim Ducheyne
064: */
065: public class ReflectionComparatorChainFactory {
066:
067: public static final ReflectionComparator STRICT_COMPARATOR = createComparatorChain(
068: LenientNumberComparator.class, SimpleCasesComparator.class,
069: CollectionComparator.class, MapComparator.class,
070: ObjectComparator.class);
071:
072: public static final ReflectionComparator IGNOREDEFAULTS_COMPARATOR = createComparatorChain(
073: IgnoreDefaultsComparator.class,
074: LenientNumberComparator.class, SimpleCasesComparator.class,
075: MapComparator.class, CollectionComparator.class,
076: ObjectComparator.class);
077:
078: public static final ReflectionComparator LENIENTDATES_COMPARATOR = createComparatorChain(
079: LenientDatesComparator.class,
080: LenientNumberComparator.class, SimpleCasesComparator.class,
081: CollectionComparator.class, MapComparator.class,
082: ObjectComparator.class);
083:
084: public static final ReflectionComparator LENIENTORDER_COMPARATOR = createComparatorChain(
085: LenientNumberComparator.class, SimpleCasesComparator.class,
086: LenientOrderCollectionComparator.class,
087: MapComparator.class, ObjectComparator.class);
088:
089: public static final ReflectionComparator IGNOREDEFAULTS_LENIENTDATES_COMPARATOR = createComparatorChain(
090: LenientDatesComparator.class,
091: IgnoreDefaultsComparator.class,
092: LenientNumberComparator.class, SimpleCasesComparator.class,
093: CollectionComparator.class, MapComparator.class,
094: ObjectComparator.class);
095:
096: public static final ReflectionComparator IGNOREDEFAULTS_LENIENTORDER_COMPARATOR = createComparatorChain(
097: IgnoreDefaultsComparator.class,
098: LenientNumberComparator.class, SimpleCasesComparator.class,
099: LenientOrderCollectionComparator.class,
100: MapComparator.class, ObjectComparator.class);
101:
102: public static final ReflectionComparator LENIENTDATES_LENIENTORDER_COMPARATOR = createComparatorChain(
103: LenientDatesComparator.class,
104: LenientNumberComparator.class, SimpleCasesComparator.class,
105: LenientOrderCollectionComparator.class,
106: MapComparator.class, ObjectComparator.class);
107:
108: public static final ReflectionComparator IGNOREDEFAULTS_LENIENTDATES_LENIENTORDER_COMPARATOR = createComparatorChain(
109: LenientDatesComparator.class,
110: IgnoreDefaultsComparator.class,
111: LenientNumberComparator.class, SimpleCasesComparator.class,
112: LenientOrderCollectionComparator.class,
113: MapComparator.class, ObjectComparator.class);
114:
115: public static final Map<Set<ReflectionComparatorMode>, ReflectionComparator> COMPARATORMODES_COMPARATOR_MAP = new HashMap<Set<ReflectionComparatorMode>, ReflectionComparator>();
116:
117: static {
118: COMPARATORMODES_COMPARATOR_MAP.put(
119: new HashSet<ReflectionComparatorMode>(),
120: STRICT_COMPARATOR);
121: COMPARATORMODES_COMPARATOR_MAP.put(asSet(IGNORE_DEFAULTS),
122: IGNOREDEFAULTS_COMPARATOR);
123: COMPARATORMODES_COMPARATOR_MAP.put(asSet(LENIENT_DATES),
124: LENIENTDATES_COMPARATOR);
125: COMPARATORMODES_COMPARATOR_MAP.put(asSet(LENIENT_ORDER),
126: LENIENTORDER_COMPARATOR);
127: COMPARATORMODES_COMPARATOR_MAP.put(asSet(IGNORE_DEFAULTS,
128: LENIENT_DATES), IGNOREDEFAULTS_LENIENTDATES_COMPARATOR);
129: COMPARATORMODES_COMPARATOR_MAP.put(asSet(IGNORE_DEFAULTS,
130: LENIENT_ORDER), IGNOREDEFAULTS_LENIENTORDER_COMPARATOR);
131: COMPARATORMODES_COMPARATOR_MAP.put(asSet(LENIENT_DATES,
132: LENIENT_ORDER), LENIENTDATES_LENIENTORDER_COMPARATOR);
133: COMPARATORMODES_COMPARATOR_MAP.put(asSet(IGNORE_DEFAULTS,
134: LENIENT_DATES, LENIENT_ORDER),
135: IGNOREDEFAULTS_LENIENTDATES_LENIENTORDER_COMPARATOR);
136: }
137:
138: public static ReflectionComparator createComparatorChain(
139: Class<? extends ReflectionComparator>... comparatorClasses) {
140: ReflectionComparator comparator = null;
141: for (int i = comparatorClasses.length - 1; i >= 0; i--) {
142: Class<? extends ReflectionComparator> comparatorClass = comparatorClasses[i];
143: try {
144: // Create a new comparator and chain it with the previous one. The first instance that is created is
145: // the last instance of the chain, and is chained with null.
146: comparator = comparatorClass.getConstructor(
147: ReflectionComparator.class).newInstance(
148: comparator);
149: } catch (Exception e) {
150: throw new UnitilsException(
151: "Error instantiating instance of "
152: + comparatorClass.getSimpleName(), e);
153: }
154: }
155: return comparator;
156: }
157:
158: public static ReflectionComparator getComparatorChainForModes(
159: ReflectionComparatorMode... modes) {
160: return COMPARATORMODES_COMPARATOR_MAP.get(asSet(modes));
161: }
162:
163: }
|