/** * Returns a map of properties that differ (via {@link Object#equals(Object)}) between two * AutoBeans. The keys are property names and the values are the value of the property in <code>b * </code>. Properties present in <code>a</code> but missing in <code>b</code> will be represented * by <code>null</code> values. This implementation will compare AutoBeans of different * parameterizations, although the diff produced is likely meaningless. * * <p>This will work for both simple and wrapper AutoBeans. * * @param a an {@link AutoBean} * @param b an {@link AutoBean} * @return a {@link Map} of differing properties */ public static Map<String, Object> diff(AutoBean<?> a, AutoBean<?> b) { // Fast check for comparing an object to itself if (a == b) { return Collections.emptyMap(); } final Map<String, Object> toReturn = getAllProperties(b); // Remove the entries that are equal, adding nulls for missing properties a.accept( new AutoBeanVisitor() { @Override public boolean visitReferenceProperty( String propertyName, AutoBean<?> previousValue, PropertyContext ctx) { if (toReturn.containsKey(propertyName)) { if (equal(propertyName, previousValue)) { // No change toReturn.remove(propertyName); } } else { // The predecessor has a value that this object doesn't. toReturn.put(propertyName, null); } return false; } @Override public boolean visitValueProperty( String propertyName, Object previousValue, PropertyContext ctx) { if (toReturn.containsKey(propertyName)) { if (equal(propertyName, previousValue)) { // No change toReturn.remove(propertyName); } } else { // The predecessor has a value that this object doesn't. toReturn.put(propertyName, null); } return false; } private boolean equal(String propertyName, AutoBean<?> previousValue) { return previousValue == null && toReturn.get(propertyName) == null || previousValue != null && equal(propertyName, previousValue.as()); } private boolean equal(String propertyName, Object previousValue) { Object currentValue = toReturn.get(propertyName); return previousValue == null && currentValue == null || previousValue != null && previousValue.equals(currentValue); } }); return toReturn; }
/** * Returns a map that is a copy of the properties contained in an AutoBean. The returned map is * mutable, but editing it will not have any effect on the bean that produced it. * * @param bean an {@link AutoBean} * @return a {@link Map} of the bean's properties */ public static Map<String, Object> getAllProperties(AutoBean<?> bean) { final Map<String, Object> toReturn = new LinkedHashMap<String, Object>(); // Look at the previous value of all properties bean.accept( new AutoBeanVisitor() { @Override public boolean visitReferenceProperty( String propertyName, AutoBean<?> value, PropertyContext ctx) { toReturn.put(propertyName, value == null ? null : value.as()); return false; } @Override public boolean visitValueProperty( String propertyName, Object value, PropertyContext ctx) { toReturn.put(propertyName, value); return false; } }); return toReturn; }