All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.crazyyak.dev.common.EqualsUtils Maven / Gradle / Ivy

There is a newer version: 4.1.4
Show newest version
package org.crazyyak.dev.common;

import org.joda.time.LocalDate;
import org.joda.time.LocalDateTime;
import org.joda.time.LocalTime;

import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.*;

public class EqualsUtils {

  private EqualsUtils() {
  }

  /** Helper method for testing equality of objects while handling the issue of nulls */
  public static  boolean objectsEqual(T valueA, T valueB) {
    if (valueA == valueB) {
      return true; // Same instance or both null
    } else if (valueA == null || valueB == null) {
      return false; // One but not both are null
    } else {
      return valueA.equals(valueB);
    }
  }

  public static  boolean objectsNotEqual(T valueA, T valueB) {
    return objectsEqual(valueA, valueB) == false;
  }

  static public boolean datesEqual(Date dateA, Date dateB) {
    if (dateA == null && dateB == null) {
      return true;
    } else if (dateA == null || dateB == null) {
      return false;
    } else {
      GregorianCalendar calA = new GregorianCalendar();
      calA.setTime(dateA);
      GregorianCalendar calB = new GregorianCalendar();
      calB.setTime(dateB);

      return (calA.get(Calendar.DATE) == calB.get(Calendar.DATE) &&
        calA.get(Calendar.MONTH) == calB.get(Calendar.MONTH) &&
        calA.get(Calendar.YEAR) == calB.get(Calendar.YEAR));
    }
  }

  static public boolean datesNotEqual(Date dateA, Date dateB) {
    return datesEqual(dateA, dateB) == false;
  }

  public static List compare(Object objectA, Object objectB) {
    List results = new ArrayList<>();
    return compare(results, null, objectA, objectB, "root");
  }

  private static List compare(List results, Field field, Object objectA, Object objectB, final String beanName) {

    Object valueA = (field == null) ? objectA : ReflectUtils.getFieldValue(objectA, field);
    Object valueB = (field == null) ? objectB : ReflectUtils.getFieldValue(objectB, field);

    if (valueA == valueB) {
      return results;

    } else if (valueA == null) {
      return build(results, beanName, "Object A is null", "-some value-");

    } else if (valueB == null) {
      return build(results, beanName, "-some value-", "Object B is null");

    } else if (valueA.getClass().equals(valueB.getClass()) == false) {
      return build(results, beanName, valueA.getClass().getName(), valueB.getClass().getName());

    }

    Class type = valueA.getClass();
    if (String.class.equals(type)) {
      System.out.print("");
    }

    if (isPrimitive(type)) {
      if (EqualsUtils.objectsEqual(valueA, valueB)) {
        return results;
      } else {
        return build(results, beanName, valueA, valueB);
      }

    } else if (type.isArray()) {
      List collectionA = Arrays.asList((Object[]) valueA);
      List collectionB = Arrays.asList((Object[]) valueB);
      return compareCollection(results, collectionA, collectionB, beanName);

    } else if (valueA instanceof Collection && valueB instanceof Collection) {
      Collection collectionA = (Collection)valueA;
      Collection collectionB = (Collection)valueB;
      return compareCollection(results, new ArrayList(collectionA), new ArrayList(collectionB), beanName);

    } else if (valueA instanceof Map && valueB instanceof Map) {
      return compareMaps(results, (Map) valueA, (Map) valueB, beanName);

    }

    List fields = ReflectUtils.getFields(valueA.getClass());
    for (Field nextField : fields) {
      boolean isStatic = Modifier.isStatic(nextField.getModifiers());
      boolean isTransient = Modifier.isTransient(nextField.getModifiers());
      if (isStatic == false && isTransient == false) {
        // no point in comparing static or transient values
        compare(results, nextField, valueA, valueB, beanName + "." + nextField.getName());
      }
    }

    return results;
  }

  private static List compareMaps(List results, Map mapA, Map mapB, String beanName) {

    int countA = mapA.size();
    int countB = mapB.size();

    if (countA != countB) {
      build(results, beanName+":count()", countA, countB);
    }

    Set keysA = mapA.keySet();
    Set keysB = mapB.keySet();
    List intersection = BeanUtils.intersection(keysA, keysB);

    for (Object key : intersection) {
      Object valueA = mapA.get(key);
      Object valueB = mapB.get(key);
      compare(results, null, valueA, valueB, beanName+"["+key+"]");
    }

    for (Object key : mapA.keySet()) {
      if (intersection.contains(key) == false) {
        compare(results, null, "-contained-", "-missing-", beanName+"["+key+"]");
      }
    }

    for (Object key : mapB.keySet()) {
      if (intersection.contains(key) == false) {
        compare(results, null, "-missing-", "-contained-", beanName+"["+key+"]");
      }
    }

    return results;
  }

  private static List build(List result, String message, Object valueA, Object valueB) {
    String msg = String.format("%s:\n  %s\n  %s", message, valueA, valueB);
    result.add(msg);
    return result;
  }

  private static List compareCollection(List results, List listA, List listB, String beanName) {

    int countA = listA.size();
    int countB = listB.size();

    if (countA != countB) {
      build(results, beanName+":count()", countA, countB);
    }

    int max = Math.min(countA, countB);
    for (int i = 0; i < max; i++) {
      Object valueA = listA.get(i);
      Object valueB = listB.get(i);
      compare(results, null, valueA, valueB, beanName+"["+i+"]");
    }

    for (int i = max; i < listA.size(); i++) {
      compare(results, null, "-contained-", "-missing-", beanName+"["+i+"]");
    }

    for (int i = max; i < listB.size(); i++) {
      compare(results, null, "-missing-", "-contained-", beanName+"["+i+"]");
    }

    return results;
  }

  private static boolean isPrimitive(Class type) {

    List types = Arrays.asList(
        Boolean.class,
        Character.class, Byte.class, Short.class,
        Integer.class, Long.class, Float.class, Double.class,
        String.class,
        LocalDate.class, LocalTime.class, LocalDateTime.class
    );

    return type.isPrimitive() || types.contains(type);

  }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy