1 4 package com.tc.util.diff; 5 6 import org.apache.commons.lang.builder.EqualsBuilder; 7 8 import com.tc.util.Assert; 9 import com.tc.util.StandardStringifier; 10 import com.tc.util.Stringifier; 11 12 import java.lang.reflect.AccessibleObject ; 13 import java.lang.reflect.Field ; 14 import java.lang.reflect.Modifier ; 15 import java.util.ArrayList ; 16 import java.util.Iterator ; 17 import java.util.List ; 18 19 23 public class DifferenceBuilder { 24 25 private static final Stringifier DEFAULT_STRINGIFIER = StandardStringifier.INSTANCE; 27 28 public static String describeDifferences(Differenceable a, Differenceable b) { 29 return describeDifferences(a, b, DEFAULT_STRINGIFIER); 30 } 31 32 public static String describeDifferences(Differenceable a, Differenceable b, Stringifier stringifier) { 33 Difference[] differences = getDifferencesAsArray(a, b); 34 if (differences.length == 0) return ""; 35 36 StringBuffer descrip = new StringBuffer (); 37 descrip.append("differences are:\n"); 38 for (int i = 0; i < differences.length; ++i) { 39 descrip.append(differences[i].toString()); 40 descrip.append("\n"); 41 } 42 43 return descrip.toString(); 44 } 45 46 public static Iterator getDifferences(Differenceable a, Differenceable b, Stringifier stringifier) { 47 Assert.assertNotNull(a); 48 Assert.assertNotNull(b); 49 50 DifferenceContext context = DifferenceContext.createInitial(stringifier); 51 52 if (! (a.getClass().equals(b.getClass()))) { 53 context.addDifference(new BasicObjectDifference(context, a, b)); 54 } else { 55 a.addDifferences(context, b); 56 } 57 58 return context.getDifferences(); 59 } 60 61 public static Iterator getDifferences(Differenceable a, Differenceable b) { 62 return getDifferences(a, b, DEFAULT_STRINGIFIER); 63 } 64 65 public static Difference[] getDifferencesAsArray(Differenceable a, Differenceable b) { 66 return getDifferencesAsArray(a, b, DEFAULT_STRINGIFIER); 67 } 68 69 public static Difference[] getDifferencesAsArray(Differenceable a, Differenceable b, Stringifier stringifier) { 70 List out = new ArrayList (); 71 Iterator i = getDifferences(a, b, stringifier); 72 while (i.hasNext()) { 73 out.add(i.next()); 74 } 75 76 return (Difference[]) out.toArray(new Difference[out.size()]); 77 } 78 79 private final DifferenceContext previous; 80 81 public DifferenceBuilder(DifferenceContext previous) { 82 Assert.assertNotNull(previous); 83 84 this.previous = previous; 85 } 86 87 public DifferenceBuilder reflectionDifference(Object a, Object b) { 88 Assert.assertNotNull(a); 89 Assert.assertNotNull(b); 90 Assert.eval(a instanceof Differenceable); 91 Assert.eval(b instanceof Differenceable); 92 Assert.eval(a.getClass().isInstance(b)); 93 94 if (a == b) return this; 95 96 Field [] fields = a.getClass().getDeclaredFields(); 97 AccessibleObject.setAccessible(fields, true); 98 for (int i = 0; i < fields.length; ++i) { 99 Field f = fields[i]; 100 if (!Modifier.isStatic(f.getModifiers())) { 101 try { 102 doReflectiveAppend(f.getName(), f.get(a), f.get(b), f.getType()); 103 } catch (IllegalAccessException iae) { 104 throw Assert.failure("This should be impossible", iae); 106 } 107 } 108 } 109 110 return this; 111 } 112 113 private void doReflectiveAppend(String context, Object a, Object b, Class c) { 114 if (c.isPrimitive()) { 115 if (c.equals(Boolean.TYPE)) append(context, ((Boolean ) a).booleanValue(), ((Boolean ) b).booleanValue()); 116 else if (c.equals(Character.TYPE)) append(context, ((Character ) a).charValue(), ((Character ) b).charValue()); 117 else if (c.equals(Byte.TYPE)) append(context, ((Byte ) a).byteValue(), ((Byte ) b).byteValue()); 118 else if (c.equals(Short.TYPE)) append(context, ((Short ) a).shortValue(), ((Short ) b).shortValue()); 119 else if (c.equals(Integer.TYPE)) append(context, ((Integer ) a).intValue(), ((Integer ) b).intValue()); 120 else if (c.equals(Long.TYPE)) append(context, ((Long ) a).longValue(), ((Long ) b).longValue()); 121 else if (c.equals(Float.TYPE)) append(context, ((Float ) a).floatValue(), ((Float ) b).floatValue()); 122 else if (c.equals(Double.TYPE)) append(context, ((Double ) a).doubleValue(), ((Double ) b).doubleValue()); 123 else throw Assert.failure("Unknown primitive type " + c); 124 } else { 125 append(context, a, b); 126 } 127 } 128 129 public DifferenceBuilder append(String context, boolean a, boolean b) { 130 if (a != b) add(new PrimitiveDifference(this.previous.sub(context), a, b)); 131 return this; 132 } 133 134 public DifferenceBuilder append(String context, char a, char b) { 135 if (a != b) add(new PrimitiveDifference(this.previous.sub(context), a, b)); 136 return this; 137 } 138 139 public DifferenceBuilder append(String context, byte a, byte b) { 140 if (a != b) add(new PrimitiveDifference(this.previous.sub(context), a, b)); 141 return this; 142 } 143 144 public DifferenceBuilder append(String context, short a, short b) { 145 if (a != b) add(new PrimitiveDifference(this.previous.sub(context), a, b)); 146 return this; 147 } 148 149 public DifferenceBuilder append(String context, int a, int b) { 150 if (a != b) add(new PrimitiveDifference(this.previous.sub(context), a, b)); 151 return this; 152 } 153 154 public DifferenceBuilder append(String context, long a, long b) { 155 if (a != b) add(new PrimitiveDifference(this.previous.sub(context), a, b)); 156 return this; 157 } 158 159 public DifferenceBuilder append(String context, float a, float b) { 160 if (a != b) add(new PrimitiveDifference(this.previous.sub(context), a, b)); 161 return this; 162 } 163 164 public DifferenceBuilder append(String context, double a, double b) { 165 if (a != b) add(new PrimitiveDifference(this.previous.sub(context), a, b)); 166 return this; 167 } 168 169 public DifferenceBuilder append(String context, Object a, Object b) { 170 if (a != null && b != null) { 171 if ((a instanceof Differenceable) && (b instanceof Differenceable) && 172 (a.getClass().equals(b.getClass()))) { 173 handleDifferenceables(context, a, b); 174 } else if (a.getClass().isArray() && b.getClass().isArray()) { 175 handleArrays(context, a, b); 176 } else if (!(a.equals(b))) { 177 add(new BasicObjectDifference(this.previous.sub(context), a, b)); 178 } 179 } else if (a != null || b != null) { 180 add(new BasicObjectDifference(this.previous.sub(context), a, b)); 181 } 182 183 return this; 184 } 185 186 private void handleArrays(String context, Object a, Object b) { 187 if ((a.getClass().getComponentType().isPrimitive() || b.getClass().getComponentType().isPrimitive()) 188 && (!a.getClass().getComponentType().equals(b.getClass().getComponentType()))) { 189 add(new BasicObjectDifference(this.previous.sub(context), a, b)); 190 } 191 192 if (a.getClass().getComponentType().isPrimitive()) { 193 if (a instanceof boolean[]) { 194 handleArrays(context, (boolean[]) a, (boolean[]) b); 195 } else if (a instanceof char[]) { 196 handleArrays(context, (char[]) a, (char[]) b); 197 } else if (a instanceof byte[]) { 198 handleArrays(context, (byte[]) a, (byte[]) b); 199 } else if (a instanceof short[]) { 200 handleArrays(context, (short[]) a, (short[]) b); 201 } else if (a instanceof int[]) { 202 handleArrays(context, (int[]) a, (int[]) b); 203 } else if (a instanceof long[]) { 204 handleArrays(context, (long[]) a, (long[]) b); 205 } else if (a instanceof float[]) { 206 handleArrays(context, (float[]) a, (float[]) b); 207 } else if (a instanceof double[]) { 208 handleArrays(context, (double[]) a, (double[]) b); 209 } else { 210 throw Assert.failure("Unknown primitive type " + a.getClass().getComponentType()); 211 } 212 } else { 213 handleArrays(context, (Object []) a, (Object []) b); 214 } 215 } 216 217 private void handleArrays(String context, boolean[] a, boolean[] b) { 218 if (a.length != b.length) { 219 add(new PrimitiveDifference(this.previous.sub(context + ".length"), a.length, b.length)); 220 } else { 221 for (int i = 0; i < a.length; ++i) { 222 if (a[i] != b[i]) add(new PrimitiveDifference(this.previous.sub(context + "[" + i + "]"), a[i], b[i])); 223 } 224 } 225 } 226 227 private void handleArrays(String context, char[] a, char[] b) { 228 if (a.length != b.length) { 229 add(new PrimitiveDifference(this.previous.sub(context + ".length"), a.length, b.length)); 230 } else { 231 for (int i = 0; i < a.length; ++i) { 232 if (a[i] != b[i]) add(new PrimitiveDifference(this.previous.sub(context + "[" + i + "]"), a[i], b[i])); 233 } 234 } 235 } 236 237 private void handleArrays(String context, byte[] a, byte[] b) { 238 if (a.length != b.length) { 239 add(new PrimitiveDifference(this.previous.sub(context + ".length"), a.length, b.length)); 240 } else { 241 for (int i = 0; i < a.length; ++i) { 242 if (a[i] != b[i]) add(new PrimitiveDifference(this.previous.sub(context + "[" + i + "]"), a[i], b[i])); 243 } 244 } 245 } 246 247 private void handleArrays(String context, short[] a, short[] b) { 248 if (a.length != b.length) { 249 add(new PrimitiveDifference(this.previous.sub(context + ".length"), a.length, b.length)); 250 } else { 251 for (int i = 0; i < a.length; ++i) { 252 if (a[i] != b[i]) add(new PrimitiveDifference(this.previous.sub(context + "[" + i + "]"), a[i], b[i])); 253 } 254 } 255 } 256 257 private void handleArrays(String context, int[] a, int[] b) { 258 if (a.length != b.length) { 259 add(new PrimitiveDifference(this.previous.sub(context + ".length"), a.length, b.length)); 260 } else { 261 for (int i = 0; i < a.length; ++i) { 262 if (a[i] != b[i]) add(new PrimitiveDifference(this.previous.sub(context + "[" + i + "]"), a[i], b[i])); 263 } 264 } 265 } 266 267 private void handleArrays(String context, long[] a, long[] b) { 268 if (a.length != b.length) { 269 add(new PrimitiveDifference(this.previous.sub(context + ".length"), a.length, b.length)); 270 } else { 271 for (int i = 0; i < a.length; ++i) { 272 if (a[i] != b[i]) add(new PrimitiveDifference(this.previous.sub(context + "[" + i + "]"), a[i], b[i])); 273 } 274 } 275 } 276 277 private void handleArrays(String context, float[] a, float[] b) { 278 if (a.length != b.length) { 279 add(new PrimitiveDifference(this.previous.sub(context + ".length"), a.length, b.length)); 280 } else { 281 for (int i = 0; i < a.length; ++i) { 282 if (a[i] != b[i]) add(new PrimitiveDifference(this.previous.sub(context + "[" + i + "]"), a[i], b[i])); 283 } 284 } 285 } 286 287 private void handleArrays(String context, double[] a, double[] b) { 288 if (a.length != b.length) { 289 add(new PrimitiveDifference(this.previous.sub(context + ".length"), a.length, b.length)); 290 } else { 291 for (int i = 0; i < a.length; ++i) { 292 if (a[i] != b[i]) add(new PrimitiveDifference(this.previous.sub(context + "[" + i + "]"), a[i], b[i])); 293 } 294 } 295 } 296 297 private void handleArrays(String context, Object [] a, Object [] b) { 298 if (a.length != b.length) { 299 add(new PrimitiveDifference(this.previous.sub(context + ".length"), a.length, b.length)); 300 } else { 301 for (int i = 0; i < a.length; ++i) { 302 if ((a[i] == null) != (b[i] == null)) { 303 add(new BasicObjectDifference(this.previous.sub(context + "[" + i + "]"), a[i], b[i])); 304 } else if (a[i] != null) { 305 this.append(context + "[" + i + "]", a[i], b[i]); 306 } 307 } 308 } 309 } 310 311 private void handleDifferenceables(String context, Object a, Object b) { 312 int preSize = countDifferences(); 313 ((Differenceable) a).addDifferences(this.previous.sub(context), b); 314 boolean hadDifferences = (countDifferences() - preSize) > 0; 315 316 boolean equals = a.equals(b); 317 Assert.eval("differences added (" + hadDifferences + ") is not same as equals(" + equals + ")", 318 hadDifferences != equals); 319 } 320 321 private void add(Difference difference) { 322 difference.where().addDifference(difference); 323 } 324 325 private int countDifferences() { 326 return this.previous.countDifferences(); 327 } 328 329 } | Popular Tags |