blob: aeb6d74b99e70276b91ee18eb82268b9499a5676 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2015 Ericsson
*
* All rights reserved. This program and the accompanying materials are
* made available under the terms of the Eclipse Public License 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* Patrick Tasse - Initial API and implementation
*******************************************************************************/
package org.eclipse.tracecompass.common.core;
import java.lang.reflect.Array;
import java.util.Collection;
import java.util.Iterator;
import java.util.Set;
import java.util.SortedSet;
import org.eclipse.jdt.annotation.Nullable;
/**
* Utility methods for arbitrary objects.
*
* @author Patrick Tasse
* @since 2.0
*/
public final class ObjectUtils {
private ObjectUtils() {}
/**
* Checks deep equality of two objects that may be arrays or collections.
* The objects (and each of their respective elements, if applicable) must
* be of the same class to be considered equal. Deep equality is recursively
* called on each element of an array or collection, if applicable.
*
* @param o1
* the first object to compare
* @param o2
* the second object to compare
* @return true if the objects are deeply equal, false otherwise
*/
public static boolean deepEquals(final @Nullable Object o1, @Nullable final Object o2) {
if (o1 == o2) {
return true;
}
if (o1 == null || o2 == null) {
return false;
}
Class<?> class1 = o1.getClass();
Class<?> class2 = o2.getClass();
if (!class1.equals(class2)) {
return false;
}
if (class1.isArray()) {
int length = Array.getLength(o1);
if (Array.getLength(o2) != length) {
return false;
}
for (int i = 0; i < length; i++) {
if (!deepEquals(Array.get(o1, i), Array.get(o2, i))) {
return false;
}
}
return true;
} else if (o1 instanceof Collection) {
Collection<?> c1 = (Collection<?>) o1;
Collection<?> c2 = (Collection<?>) o2;
int size = c1.size();
if (c2.size() != size) {
return false;
}
if (o1 instanceof Set && !(o1 instanceof SortedSet<?>)) {
/* Iteration order is undefined */
for (Object e1 : c1) {
boolean contains = false;
for (Object e2 : c2) {
if (deepEquals(e1, e2)) {
contains = true;
break;
}
}
if (!contains) {
return false;
}
}
return true;
}
Iterator<?> i1 = c1.iterator();
Iterator<?> i2 = c2.iterator();
while (i1.hasNext()) {
if (!deepEquals(i1.next(), i2.next())) {
return false;
}
}
return true;
}
return o1.equals(o2);
}
/**
* Returns a hash code value for an object which may be an array or a
* collection. The deep hash code is recursively computed for each element
* of an array or collection, if applicable.
*
* @param o
* the object
* @return a hash code value for this object.
*/
public static int deepHashCode(final @Nullable Object o) {
if (o == null) {
return 0;
}
if (o.getClass().isArray()) {
final int prime = 31;
int result = 1;
for (int i = 0; i < Array.getLength(o); i++) {
result = prime * result + deepHashCode(Array.get(o, i));
}
return result;
}
if (o instanceof Collection) {
Collection<?> c = (Collection<?>) o;
if (o instanceof Set && !(o instanceof SortedSet<?>)) {
/* Iteration order is undefined */
int result = 0;
for (Object e : c) {
result += deepHashCode(e);
}
return result;
}
final int prime = 31;
int result = 1;
for (Object e : c) {
result = prime * result + deepHashCode(e);
}
return result;
}
return o.hashCode();
}
}