Beispiel #1
0
  /**
   * 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;
  }
Beispiel #2
0
  /**
   * 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);
    }
  }
Beispiel #3
0
  /**
   * 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;
  }