/** Compare two AutoBeans, this method has the type fan-out. */ static boolean sameOrEquals( Object value, Object otherValue, Map<PendingComparison, Comparison> pending) { if (value == otherValue) { // Fast exit return true; } if (value instanceof Collection<?> && otherValue instanceof Collection<?>) { // Check collections return sameOrEquals((Collection<?>) value, (Collection<?>) otherValue, pending, null); } if (value instanceof Map<?, ?> && otherValue instanceof Map<?, ?>) { // Check maps return sameOrEquals((Map<?, ?>) value, (Map<?, ?>) otherValue, pending); } if (value instanceof Splittable && otherValue instanceof Splittable) { return sameOrEquals((Splittable) value, (Splittable) otherValue, pending); } // Possibly substitute the AutoBean for its shim { AutoBean<?> maybeValue = AutoBeanUtils.getAutoBean(value); AutoBean<?> maybeOther = AutoBeanUtils.getAutoBean(otherValue); if (maybeValue != null && maybeOther != null) { value = maybeValue; otherValue = maybeOther; } } if (value instanceof AutoBean<?> && otherValue instanceof AutoBean<?>) { // Check ValueProxies return sameOrEquals((AutoBean<?>) value, (AutoBean<?>) otherValue, pending); } if (value == null ^ otherValue == null) { // One is null, the other isn't return false; } if (value != null && !value.equals(otherValue)) { // Regular object equality return false; } return true; }
/** * If a comparison between two AutoBeans is currently pending, this method will skip their * comparison. */ private static boolean sameOrEquals( AutoBean<?> value, AutoBean<?> otherValue, Map<PendingComparison, Comparison> pending) { if (value == otherValue) { // Simple case return true; } else if (!value.getType().equals(otherValue.getType())) { // Beans of different types return false; } /* * The PendingComparison key allows us to break reference cycles when * crawling the graph. Since the entire operation is essentially a * concatenated && operation, it's ok to speculatively return true for * repeated a.equals(b) tests. */ PendingComparison key = new PendingComparison(value, otherValue); Comparison previous = pending.get(key); if (previous == null) { // Prevent the same comparison from being made pending.put(key, Comparison.PENDING); // Compare each property Map<String, Object> beanProperties = AutoBeanUtils.getAllProperties(value); Map<String, Object> otherProperties = AutoBeanUtils.getAllProperties(otherValue); for (Map.Entry<String, Object> entry : beanProperties.entrySet()) { Object property = entry.getValue(); Object otherProperty = otherProperties.get(entry.getKey()); if (!sameOrEquals(property, otherProperty, pending)) { pending.put(key, Comparison.FALSE); return false; } } pending.put(key, Comparison.TRUE); return true; } else { // Return true for TRUE or PENDING return !Comparison.FALSE.equals(previous); } }