001: /*
002: * All content copyright (c) 2003-2006 Terracotta, Inc., except as may otherwise be noted in a separate copyright notice. All rights reserved.
003: */
004: package com.tc.util.diff;
005:
006: import org.apache.commons.lang.builder.EqualsBuilder;
007:
008: import com.tc.util.Assert;
009: import com.tc.util.StandardStringifier;
010: import com.tc.util.Stringifier;
011:
012: import java.lang.reflect.AccessibleObject;
013: import java.lang.reflect.Field;
014: import java.lang.reflect.Modifier;
015: import java.util.ArrayList;
016: import java.util.Iterator;
017: import java.util.List;
018:
019: /**
020: * An object like {@link EqualsBuilder}, but for use building the implementation of
021: * {@link Differenceable.addDifferences}instead.
022: */
023: public class DifferenceBuilder {
024:
025: // private static final String DIFFERENT_CLASS_CONTEXT = "#class";
026: private static final Stringifier DEFAULT_STRINGIFIER = StandardStringifier.INSTANCE;
027:
028: public static String describeDifferences(Differenceable a,
029: Differenceable b) {
030: return describeDifferences(a, b, DEFAULT_STRINGIFIER);
031: }
032:
033: public static String describeDifferences(Differenceable a,
034: Differenceable b, Stringifier stringifier) {
035: Difference[] differences = getDifferencesAsArray(a, b);
036: if (differences.length == 0)
037: return "";
038:
039: StringBuffer descrip = new StringBuffer();
040: descrip.append("differences are:\n");
041: for (int i = 0; i < differences.length; ++i) {
042: descrip.append(differences[i].toString());
043: descrip.append("\n");
044: }
045:
046: return descrip.toString();
047: }
048:
049: public static Iterator getDifferences(Differenceable a,
050: Differenceable b, Stringifier stringifier) {
051: Assert.assertNotNull(a);
052: Assert.assertNotNull(b);
053:
054: DifferenceContext context = DifferenceContext
055: .createInitial(stringifier);
056:
057: if (!(a.getClass().equals(b.getClass()))) {
058: context.addDifference(new BasicObjectDifference(context, a,
059: b));
060: } else {
061: a.addDifferences(context, b);
062: }
063:
064: return context.getDifferences();
065: }
066:
067: public static Iterator getDifferences(Differenceable a,
068: Differenceable b) {
069: return getDifferences(a, b, DEFAULT_STRINGIFIER);
070: }
071:
072: public static Difference[] getDifferencesAsArray(Differenceable a,
073: Differenceable b) {
074: return getDifferencesAsArray(a, b, DEFAULT_STRINGIFIER);
075: }
076:
077: public static Difference[] getDifferencesAsArray(Differenceable a,
078: Differenceable b, Stringifier stringifier) {
079: List out = new ArrayList();
080: Iterator i = getDifferences(a, b, stringifier);
081: while (i.hasNext()) {
082: out.add(i.next());
083: }
084:
085: return (Difference[]) out.toArray(new Difference[out.size()]);
086: }
087:
088: private final DifferenceContext previous;
089:
090: public DifferenceBuilder(DifferenceContext previous) {
091: Assert.assertNotNull(previous);
092:
093: this .previous = previous;
094: }
095:
096: public DifferenceBuilder reflectionDifference(Object a, Object b) {
097: Assert.assertNotNull(a);
098: Assert.assertNotNull(b);
099: Assert.eval(a instanceof Differenceable);
100: Assert.eval(b instanceof Differenceable);
101: Assert.eval(a.getClass().isInstance(b));
102:
103: if (a == b)
104: return this ;
105:
106: Field[] fields = a.getClass().getDeclaredFields();
107: AccessibleObject.setAccessible(fields, true);
108: for (int i = 0; i < fields.length; ++i) {
109: Field f = fields[i];
110: if (!Modifier.isStatic(f.getModifiers())) {
111: try {
112: doReflectiveAppend(f.getName(), f.get(a), f.get(b),
113: f.getType());
114: } catch (IllegalAccessException iae) {
115: // We should've gotten a SecurityException above, instead.
116: throw Assert.failure("This should be impossible",
117: iae);
118: }
119: }
120: }
121:
122: return this ;
123: }
124:
125: private void doReflectiveAppend(String context, Object a, Object b,
126: Class c) {
127: if (c.isPrimitive()) {
128: if (c.equals(Boolean.TYPE))
129: append(context, ((Boolean) a).booleanValue(),
130: ((Boolean) b).booleanValue());
131: else if (c.equals(Character.TYPE))
132: append(context, ((Character) a).charValue(),
133: ((Character) b).charValue());
134: else if (c.equals(Byte.TYPE))
135: append(context, ((Byte) a).byteValue(), ((Byte) b)
136: .byteValue());
137: else if (c.equals(Short.TYPE))
138: append(context, ((Short) a).shortValue(), ((Short) b)
139: .shortValue());
140: else if (c.equals(Integer.TYPE))
141: append(context, ((Integer) a).intValue(), ((Integer) b)
142: .intValue());
143: else if (c.equals(Long.TYPE))
144: append(context, ((Long) a).longValue(), ((Long) b)
145: .longValue());
146: else if (c.equals(Float.TYPE))
147: append(context, ((Float) a).floatValue(), ((Float) b)
148: .floatValue());
149: else if (c.equals(Double.TYPE))
150: append(context, ((Double) a).doubleValue(),
151: ((Double) b).doubleValue());
152: else
153: throw Assert.failure("Unknown primitive type " + c);
154: } else {
155: append(context, a, b);
156: }
157: }
158:
159: public DifferenceBuilder append(String context, boolean a, boolean b) {
160: if (a != b)
161: add(new PrimitiveDifference(this .previous.sub(context), a,
162: b));
163: return this ;
164: }
165:
166: public DifferenceBuilder append(String context, char a, char b) {
167: if (a != b)
168: add(new PrimitiveDifference(this .previous.sub(context), a,
169: b));
170: return this ;
171: }
172:
173: public DifferenceBuilder append(String context, byte a, byte b) {
174: if (a != b)
175: add(new PrimitiveDifference(this .previous.sub(context), a,
176: b));
177: return this ;
178: }
179:
180: public DifferenceBuilder append(String context, short a, short b) {
181: if (a != b)
182: add(new PrimitiveDifference(this .previous.sub(context), a,
183: b));
184: return this ;
185: }
186:
187: public DifferenceBuilder append(String context, int a, int b) {
188: if (a != b)
189: add(new PrimitiveDifference(this .previous.sub(context), a,
190: b));
191: return this ;
192: }
193:
194: public DifferenceBuilder append(String context, long a, long b) {
195: if (a != b)
196: add(new PrimitiveDifference(this .previous.sub(context), a,
197: b));
198: return this ;
199: }
200:
201: public DifferenceBuilder append(String context, float a, float b) {
202: if (a != b)
203: add(new PrimitiveDifference(this .previous.sub(context), a,
204: b));
205: return this ;
206: }
207:
208: public DifferenceBuilder append(String context, double a, double b) {
209: if (a != b)
210: add(new PrimitiveDifference(this .previous.sub(context), a,
211: b));
212: return this ;
213: }
214:
215: public DifferenceBuilder append(String context, Object a, Object b) {
216: if (a != null && b != null) {
217: if ((a instanceof Differenceable)
218: && (b instanceof Differenceable)
219: && (a.getClass().equals(b.getClass()))) {
220: handleDifferenceables(context, a, b);
221: } else if (a.getClass().isArray() && b.getClass().isArray()) {
222: handleArrays(context, a, b);
223: } else if (!(a.equals(b))) {
224: add(new BasicObjectDifference(this .previous
225: .sub(context), a, b));
226: }
227: } else if (a != null || b != null) {
228: add(new BasicObjectDifference(this .previous.sub(context),
229: a, b));
230: }
231:
232: return this ;
233: }
234:
235: private void handleArrays(String context, Object a, Object b) {
236: if ((a.getClass().getComponentType().isPrimitive() || b
237: .getClass().getComponentType().isPrimitive())
238: && (!a.getClass().getComponentType().equals(
239: b.getClass().getComponentType()))) {
240: add(new BasicObjectDifference(this .previous.sub(context),
241: a, b));
242: }
243:
244: if (a.getClass().getComponentType().isPrimitive()) {
245: if (a instanceof boolean[]) {
246: handleArrays(context, (boolean[]) a, (boolean[]) b);
247: } else if (a instanceof char[]) {
248: handleArrays(context, (char[]) a, (char[]) b);
249: } else if (a instanceof byte[]) {
250: handleArrays(context, (byte[]) a, (byte[]) b);
251: } else if (a instanceof short[]) {
252: handleArrays(context, (short[]) a, (short[]) b);
253: } else if (a instanceof int[]) {
254: handleArrays(context, (int[]) a, (int[]) b);
255: } else if (a instanceof long[]) {
256: handleArrays(context, (long[]) a, (long[]) b);
257: } else if (a instanceof float[]) {
258: handleArrays(context, (float[]) a, (float[]) b);
259: } else if (a instanceof double[]) {
260: handleArrays(context, (double[]) a, (double[]) b);
261: } else {
262: throw Assert.failure("Unknown primitive type "
263: + a.getClass().getComponentType());
264: }
265: } else {
266: handleArrays(context, (Object[]) a, (Object[]) b);
267: }
268: }
269:
270: private void handleArrays(String context, boolean[] a, boolean[] b) {
271: if (a.length != b.length) {
272: add(new PrimitiveDifference(this .previous.sub(context
273: + ".length"), a.length, b.length));
274: } else {
275: for (int i = 0; i < a.length; ++i) {
276: if (a[i] != b[i])
277: add(new PrimitiveDifference(this .previous
278: .sub(context + "[" + i + "]"), a[i], b[i]));
279: }
280: }
281: }
282:
283: private void handleArrays(String context, char[] a, char[] b) {
284: if (a.length != b.length) {
285: add(new PrimitiveDifference(this .previous.sub(context
286: + ".length"), a.length, b.length));
287: } else {
288: for (int i = 0; i < a.length; ++i) {
289: if (a[i] != b[i])
290: add(new PrimitiveDifference(this .previous
291: .sub(context + "[" + i + "]"), a[i], b[i]));
292: }
293: }
294: }
295:
296: private void handleArrays(String context, byte[] a, byte[] b) {
297: if (a.length != b.length) {
298: add(new PrimitiveDifference(this .previous.sub(context
299: + ".length"), a.length, b.length));
300: } else {
301: for (int i = 0; i < a.length; ++i) {
302: if (a[i] != b[i])
303: add(new PrimitiveDifference(this .previous
304: .sub(context + "[" + i + "]"), a[i], b[i]));
305: }
306: }
307: }
308:
309: private void handleArrays(String context, short[] a, short[] b) {
310: if (a.length != b.length) {
311: add(new PrimitiveDifference(this .previous.sub(context
312: + ".length"), a.length, b.length));
313: } else {
314: for (int i = 0; i < a.length; ++i) {
315: if (a[i] != b[i])
316: add(new PrimitiveDifference(this .previous
317: .sub(context + "[" + i + "]"), a[i], b[i]));
318: }
319: }
320: }
321:
322: private void handleArrays(String context, int[] a, int[] b) {
323: if (a.length != b.length) {
324: add(new PrimitiveDifference(this .previous.sub(context
325: + ".length"), a.length, b.length));
326: } else {
327: for (int i = 0; i < a.length; ++i) {
328: if (a[i] != b[i])
329: add(new PrimitiveDifference(this .previous
330: .sub(context + "[" + i + "]"), a[i], b[i]));
331: }
332: }
333: }
334:
335: private void handleArrays(String context, long[] a, long[] b) {
336: if (a.length != b.length) {
337: add(new PrimitiveDifference(this .previous.sub(context
338: + ".length"), a.length, b.length));
339: } else {
340: for (int i = 0; i < a.length; ++i) {
341: if (a[i] != b[i])
342: add(new PrimitiveDifference(this .previous
343: .sub(context + "[" + i + "]"), a[i], b[i]));
344: }
345: }
346: }
347:
348: private void handleArrays(String context, float[] a, float[] b) {
349: if (a.length != b.length) {
350: add(new PrimitiveDifference(this .previous.sub(context
351: + ".length"), a.length, b.length));
352: } else {
353: for (int i = 0; i < a.length; ++i) {
354: if (a[i] != b[i])
355: add(new PrimitiveDifference(this .previous
356: .sub(context + "[" + i + "]"), a[i], b[i]));
357: }
358: }
359: }
360:
361: private void handleArrays(String context, double[] a, double[] b) {
362: if (a.length != b.length) {
363: add(new PrimitiveDifference(this .previous.sub(context
364: + ".length"), a.length, b.length));
365: } else {
366: for (int i = 0; i < a.length; ++i) {
367: if (a[i] != b[i])
368: add(new PrimitiveDifference(this .previous
369: .sub(context + "[" + i + "]"), a[i], b[i]));
370: }
371: }
372: }
373:
374: private void handleArrays(String context, Object[] a, Object[] b) {
375: if (a.length != b.length) {
376: add(new PrimitiveDifference(this .previous.sub(context
377: + ".length"), a.length, b.length));
378: } else {
379: for (int i = 0; i < a.length; ++i) {
380: if ((a[i] == null) != (b[i] == null)) {
381: add(new BasicObjectDifference(this .previous
382: .sub(context + "[" + i + "]"), a[i], b[i]));
383: } else if (a[i] != null) {
384: this .append(context + "[" + i + "]", a[i], b[i]);
385: }
386: }
387: }
388: }
389:
390: private void handleDifferenceables(String context, Object a,
391: Object b) {
392: int preSize = countDifferences();
393: ((Differenceable) a).addDifferences(this .previous.sub(context),
394: b);
395: boolean hadDifferences = (countDifferences() - preSize) > 0;
396:
397: boolean equals = a.equals(b);
398: Assert.eval("differences added (" + hadDifferences
399: + ") is not same as equals(" + equals + ")",
400: hadDifferences != equals);
401: }
402:
403: private void add(Difference difference) {
404: difference.where().addDifference(difference);
405: }
406:
407: private int countDifferences() {
408: return this.previous.countDifferences();
409: }
410:
411: }
|