/** * 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); } }