blob: 280e20fd9e3dbd7f68a4a0e0b6fbce8930afdd2e [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.jdo.tck.util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Stack;
import java.math.BigDecimal;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* This is a utility class to support equality checking. An EqualityHelper
* object defines the context of a deepEquals call, because it keeps track
* of objects that have already been processed. This avoids endless
* recursion when comparing cyclic data structures for deep equality.
* <p>
* Furthermore, EqualityHelper provides convenience methods for checking
* deep equality, equality and close enough (for floating point values).
*
* @author Michael Bouschen
* @since 1.1
*/
public class EqualityHelper {
/** Logger */
protected Log logger =
LogFactory.getFactory().getInstance("org.apache.jdo.tck");
/** true if debug logging in enabled. */
protected boolean debug = logger.isDebugEnabled();
/** Used when comparing float values close enough. */
public static float FLOAT_EPSILON = (float)Math.pow(2.0, -20.0);
/** Used when comparing double values close enough. */
public static double DOUBLE_EPSILON = Math.pow(2.0, -52.0);
/** Message for null vs. not null */
static final String msgMeNull = "\nExpected null, actual not null";
/** Message for not null vs. null */
static final String msgOtherNull = "\nExpected not null, actual null";
/** Message for incompatible types */
static final String msgIncompatibleTypes = "\nIncompatible types for comparison";
/** Message for wrong class for counting via iterator */
static final String msgParameterMustBeCollectionOrMap =
"Parameter must be a Collection or Map.";
/** Comparator used in method deepEquals comparing maps. This comparator
* is used to order Maps whose keys are Comparable so the entries can be
* compared using deepCompareFields.
*/
private static Comparator entryKeyComparator = new Comparator() {
public int compare(Object o1, Object o2) {
Object key1 = ((Map.Entry)o1).getKey();
Object key2 = ((Map.Entry)o2).getKey();
return ((Comparable)key1).compareTo(key2);
}
};
/**
* Utility counter for maps and collections
*/
static int countIterator(Object o) {
int result = 0;
Iterator it;
if (o instanceof Collection) {
it = ((Collection)o).iterator();
} else if (o instanceof Map) {
it = ((Map)o).entrySet().iterator();
} else {
throw new ClassCastException(msgParameterMustBeCollectionOrMap);
}
while (it.hasNext()) {
it.next();
result++;
}
return result;
}
/** Comparator used in method deepEquals comparing maps of
* DeepEquality.
*/
private static class DeepEqualityEntryKeyComparator
implements Comparator {
Comparator comparator;
DeepEqualityEntryKeyComparator(Comparator comp) {
this.comparator = comparator;
}
public int compare(Object o1, Object o2) {
Object key1 = ((Map.Entry)o1).getKey();
Object key2 = ((Map.Entry)o2).getKey();
return comparator.compare(key1, key2);
}
}
/** Collection of instances that have been processed already in the
* context of this EqualityHelper instance
*/
private Collection processed = new HashSet();
/** StringBuffer of logged differences.
*/
StringBuffer unequalBuffer = new StringBuffer();
/**
* Context is a stack of navigational paths.
*/
Stack contextStack = new Stack();
// Methods to support keeping track of instances that have been
// processed already.
/** Returns <code>true</code> if the specified instance has been
* processed already in the context of this
* <code>EqualityHelper</code>.
* @param obj the instance to be checked.
* @return <code>true</code> if the instance has been processed
* already; <code>false</code> otherwise.
*/
public boolean isProcessed(Object obj) {
return processed.contains(obj);
}
/** Marks the specified instance as processed in the context of this
* <code>EqualityHelper</code>. This means the instance is added to the
* collection of processed instances.
* @param obj instance marked as processed
*/
public void markProcessed(Object obj) {
processed.add(obj);
}
/** Clears the collection of processed instances of this
* <code>EqualityHelper</code>. No instance is marked as processed in
* the context of this <code>EqualityHelper</code> after calling this
* method.
*/
public void clearProcessed() {
processed.clear();
}
// Deep equality support methods
/** Returns <code>true</code> if the specified instances are "deep
* equal".
* @param me one object to be tested for deep equality
* @param other the other object to be tested for deep equality
* @return <code>true</code> if the objects are deep equal.
*/
public boolean deepEquals(DeepEquality me, DeepEquality other) {
if (me == other)
return true;
if ((me == null) || (other == null))
return false;
if (isProcessed(me))
return true;
markProcessed(me);
return me.deepCompareFields(other, this);
}
/** Returns <code>true</code> if the specified instances are "deep
* equal". The method compares the two instances via the deepEquals
* method if they implement DeepEquals; compares the two instances via
* deepEquals if they implement Collection or Map, and otherwise
* compares the instances using equals.
* @param me one object to be tested for deep equality
* @param other the other object to be tested for deep equality
* @return <code>true</code> if the objects are deep equal.
*/
public boolean deepEquals(Object me, Object other) {
if (me == other)
return true;
if ((me == null) || (other == null))
return false;
if ((me instanceof DeepEquality) && (other instanceof DeepEquality))
return deepEquals((DeepEquality)me, (DeepEquality)other);
if ((me instanceof Collection) && (other instanceof Collection))
return deepEquals((Collection)me, (Collection)other);
if ((me instanceof Map) && (other instanceof Map))
return deepEquals((Map)me, (Map)other);
return me.equals(other);
}
/** Returns <code>true</code> if the specified collections are "deep
* equal". Two collections are deep equal, if they have the same size
* and their corresponding elements are deep equal after sorting
* using the natural ordering of the elements. The method throws a
* <code>ClassCastException</code> if the elements are not Comparable
* or if they are not mutually comparable.
* @param mine one collection to be tested for deep equality
* @param other the other collection to be tested for deep equality
* @return <code>true</code> if the collections are deep equal.
* @throws ClassCastException if the collections contain elements that
* are not mutually comparable.
*/
public boolean deepEquals(Collection mine, Collection other) {
if (mine == other)
return true;
if ((mine == null) || (other == null))
return false;
// Return false, if the size differs
if (mine.size() != other.size())
return false;
if (mine.size() == 0)
return true;
// Now check the elements
List myList = new ArrayList(mine);
Collections.sort(myList);
List otherList = new ArrayList(other);
/* Any collection of elements to be compared must implement Comparator
* to avoid the other side having to implement Comparable. */
Comparator comparator =
(Comparator)myList.get(0);
Collections.sort(otherList, comparator);
for (int i = 0; i < myList.size(); i++) {
if (!deepEquals(myList.get(i), otherList.get(i)))
return false;
}
return true;
}
/** Returns <code>true</code> if the specified maps are "deep
* equal". Two maps are deep equal, if they have the same size and the
* values of the corresponding keys compare deep equal. The method
* throws a <code>ClassCastException</code> if keys or values are not
* Comparable or if they are not mutually comparable.
* @param mine one map to be tested for deep equality
* @param other the other map to be tested for deep equality
* @return <code>true</code> if the maps are deep equal.
* @throws ClassCastException if the maps contain keys or values that
* are not mutually comparable.
*/
public boolean deepEquals(Map mine, Map other) {
if (mine == other)
return true;
if ((mine == null) || (other == null))
return false;
// Return false, if the size differs
if (mine.size() != other.size())
return false;
if (mine.size() == 0)
return true;
// Now check the elements
List myList = new ArrayList(mine.entrySet());
Collections.sort(myList, entryKeyComparator);
List otherList = new ArrayList(other.entrySet());
/* Any collection of elements to be compared must implement Comparator
* to avoid the other side having to implement Comparable. */
Comparator comparator =
(Comparator)((Map.Entry)myList.get(0)).getKey();
Collections.sort(otherList,
new DeepEqualityEntryKeyComparator(comparator));
for (int i = 0; i < myList.size(); i++) {
Map.Entry entry1 = (Map.Entry)myList.get(i);
Map.Entry entry2 = (Map.Entry)otherList.get(i);
// compare the keys
if (!deepEquals(entry1.getKey(), entry2.getKey()))
return false;
// compare the values
if (!deepEquals(entry1.getValue(), entry2.getValue()))
return false;
}
return true;
}
// Shallow equality support methods
/** Returns <code>true</code> if the specified collections are "shallow
* equal". Two collections are shallow equal, if they have the same size
* and their corresponding elements are equal after sorting using the
* natural ordering.
* @param mine one collection to be tested for shallow equality
* @param other the other collection to be tested for shallow equality
* @return <code>true</code> if the collections are deep equal.
*/
public boolean shallowEquals(Collection mine, Collection other) {
if (mine == other)
return true;
if ((mine == null) || (other == null))
return false;
// Return false, if the size differs
if (mine.size() != other.size())
return false;
if (mine.size() == 0)
return true;
// Now check the elements
List myList = new ArrayList(mine);
Collections.sort(myList);
List otherList = new ArrayList(other);
/* Any collection of elements to be compared must implement Comparator
* to avoid the other side having to implement Comparable. */
Comparator comparator =
(Comparator)myList.get(0);
Collections.sort(otherList, comparator);
return myList.equals(otherList);
}
// Deep equality support methods with logging
public String getUnequalBuffer() {
return unequalBuffer.toString();
}
/**
* Context is nested via navigation through relationships.
*/
void pushContext(String ctx) {
contextStack.push(ctx);
}
String popContext() {
return (String)contextStack.pop();
}
/** Log differences between objects that don't compare equal.
* @param o1 the first object
* @param o2 the second object
* @param where the field where the objects are found
*/
void logUnequal(Object o1, Object o2, String where) {
unequalBuffer.append("Context: ");
Iterator it = contextStack.iterator();
StringBuffer offset = new StringBuffer("\n");
while (it.hasNext()) {
unequalBuffer.append(it.next());
unequalBuffer.append("-> ");
offset.append(" ");
}
unequalBuffer.append(where);
unequalBuffer.append(offset.toString());
unequalBuffer.append("expected '");
unequalBuffer.append(o1);
unequalBuffer.append("'");
unequalBuffer.append(offset.toString());
unequalBuffer.append(" actual '");
unequalBuffer.append(o2);
unequalBuffer.append("'\n");
}
/** Returns <code>true</code> if the specified instances are "deep
* equal". If unequal, log the location of the inequality.
* @param me one object to be tested for deep equality
* @param other the other object to be tested for deep equality
* @param where the location of the inequality (provided by the caller)
* @return <code>true</code> if the objects are deep equal.
*/
public boolean deepEquals(DeepEquality me, Object other,
String where) {
if (me == other)
return true;
if (me == null) {
logUnequal(me, other, where + msgMeNull);
return false;
}
if (other == null) {
logUnequal(me, other, where + msgOtherNull);
return false;
}
if (isProcessed(me))
return true;
markProcessed(me);
pushContext(where);
boolean result = true;
if (!me.deepCompareFields(other, this)) {
//logUnequal(me, other, where);
result = false;
}
popContext();
return result;
}
/** Returns <code>true</code> if the specified instances are "deep
* equal". The method compares the two instances via the deepEquals
* method if they implement DeepEquals; compares the two instances via
* deepEquals if they implement Collection or Map, and otherwise
* compares the instances using equals.
* @param me one object to be tested for deep equality
* @param other the other object to be tested for deep equality
* @param where the location of the inequality (provided by the caller)
* @return <code>true</code> if the objects are deep equal.
*/
public boolean deepEquals(Object me, Object other, String where) {
if (me == other)
return true;
if (me == null) {
logUnequal(me, other, where + msgMeNull);
return false;
}
if (other == null) {
logUnequal(me, other, where + msgOtherNull);
return false;
}
if (me instanceof DeepEquality) {
return deepEquals((DeepEquality)me, other, where);
} else if ((me instanceof Collection) && (other instanceof Collection)) {
return deepEquals((Collection)me, (Collection)other, where);
} else if ((me instanceof Map) && (other instanceof Map)) {
return deepEquals((Map)me, (Map)other, where);
} else {
return equals(me, other, where);
}
}
/** Returns <code>true</code> if the specified collections are "deep
* equal". Two collections are deep equal, if they have the same size
* and their corresponding elements are deep equal after sorting
* using the natural ordering of the elements. The method throws a
* <code>ClassCastException</code> if the elements are not Comparable
* or if they are not mutually comparable.
* @param me one collection to be tested for deep equality
* @param other the other collection to be tested for deep equality
* @param where the location of the inequality (provided by the caller)
* @return <code>true</code> if the collections are deep equal.
* @throws ClassCastException if the collections contain elements that
* are not mutually comparable.
*/
public boolean deepEquals(Collection me, Collection other,
String where) {
if (me == other)
return true;
if (me == null) {
logUnequal(me, other, where + msgMeNull);
return false;
}
if (other == null) {
logUnequal(me, other, where + msgOtherNull);
return false;
}
int mysize = me.size();
int othersize = other.size();
// Return false, if the size differs
if (mysize != othersize) {
int count = countIterator(other);
logUnequal(me, other,
where + "\nSize mismatch: expected size= " + me.size() +
", original size= " + othersize +
", current size= " + other.size() +
", counted size= " + count);
return false;
}
if (mysize == 0)
return true;
// Now check each element for equality or deep equality
List myList = new ArrayList(me);
// Use the natural ordering of me; must implement Comparable
Collections.sort(myList);
List otherList = new ArrayList(other);
/* Any collection of elements to be compared must implement Comparator
* to avoid the other side having to implement Comparable. */
Comparator comparator =
(Comparator)myList.get(0);
Collections.sort(otherList, comparator);
boolean result = true;
for (int i = 0; i < myList.size(); i++) {
DeepEquality o1 = (DeepEquality)myList.get(i);
Object o2 = otherList.get(i);
/* Compare corresponding elements of the ordered list. */
if (!deepEquals(o1, o2, where + "[" + i + "]")) {
result = false;
}
}
return result;
}
/** Returns <code>true</code> if the specified maps are "deep
* equal". Two maps are deep equal, if they have the same size and the
* values of the corresponding keys compare deep equal. The method
* throws a <code>ClassCastException</code> if keys or values are not
* Comparable or if they are not mutually comparable.
* @param me one map to be tested for deep equality
* @param other the other map to be tested for deep equality
* @param where the location of the inequality (provided by the caller)
* @return <code>true</code> if the maps are deep equal.
* @throws ClassCastException if the maps contain keys or values that
* are not mutually comparable.
*/
public boolean deepEquals(Map me, Map other, String where) {
if (me == other)
return true;
if (me == null) {
logUnequal(me, other, where + msgMeNull);
return false;
}
if (other == null) {
logUnequal(me, other, where + msgOtherNull);
return false;
}
// Return false, if the size differs
int mysize = me.size();
int othersize = other.size();
if (mysize != othersize) {
int count = countIterator(other);
logUnequal(me, other,
where + "\nSize mismatch: expected size= " + me.size() +
", original size= " + othersize +
", current size= " + other.size() +
", counted size= " + count);
return false;
}
if (mysize == 0)
return true;
// Now check the elements
List myList = new ArrayList(me.entrySet());
// Use the natural ordering of me; must implement Comparable
Collections.sort(myList, entryKeyComparator);
List otherList = new ArrayList(other.entrySet());
Comparator comparator = entryKeyComparator;
// Use the Comparator to avoid the other side implementing Comparable
Object key = ((Map.Entry)myList.get(0)).getKey();
if (key instanceof Comparator) {
comparator = new DeepEqualityEntryKeyComparator((Comparator)key);
}
Collections.sort(otherList, comparator);
boolean result = true;
for (int i = 0; i < myList.size(); i++) {
Map.Entry entry1 = (Map.Entry)myList.get(i);
Object key1 = entry1.getKey();
Object value1 = entry1.getValue();
Map.Entry entry2 = (Map.Entry)otherList.get(i);
Object key2 = entry2.getKey();
Object value2 = entry2.getValue();
// compare the keys
if (!deepEquals(key1, key2, where + "[" + i + "].key")) {
result = false;
}
// compare the values
if (!deepEquals(value1, value2, where + "[" + i + "].value")) {
result = false;
}
}
return result;
}
// Shallow equality support methods
/** Returns <code>true</code> if the specified collections are "shallow
* equal". Two collections are shallow equal, if they have the same size
* and their corresponding elements are equal after sorting using the
* natural ordering.
* @param me one collection to be tested for shallow equality
* @param other the other collection to be tested for shallow equality
* @param where the location of the inequality (provided by the caller)
* @return <code>true</code> if the collections are deep equal.
*/
public boolean shallowEquals(Collection me, Collection other,
String where) {
if (me == other)
return true;
if (me == null) {
logUnequal(me, other, where + msgMeNull);
return false;
}
if (other == null) {
logUnequal(me, other, where + msgOtherNull);
return false;
}
// Return false, if the size differs
int mysize = me.size();
int othersize = other.size();
if (mysize != othersize) {
// debug size...
Iterator it = other.iterator();
int count = 0;
while (it.hasNext()) { it.next(); ++count; }
logUnequal(me, other,
where + "\nSize mismatch: expected size= " + me.size() +
", original size= " + othersize +
", current size= " + other.size() +
", counted size= " + count);
return false;
}
if (me.size() == 0)
return true;
// Now check the elements
List myList = new ArrayList(me);
Collections.sort(myList);
List otherList = new ArrayList(other);
/* Any collection of elements to be compared must implement Comparator
* to avoid the other side having to implement Comparable. */
Comparator comparator =
(Comparator)myList.get(0);
Collections.sort(otherList, comparator);
boolean result = myList.equals(otherList);
if (!result)
logUnequal(me, other,
where + "\nCollections do not compare equal");
return result;
}
// Equality support methods
/** Returns <code>true</code> if the specified objects are equal.
* This is a helper method checking for identical and <code>null</code>
* objects before delegating to the regular equals method.
* @param o1 one object to be tested for equality
* @param o2 the other object to be tested for equality
* @param where the location of the inequality (provided by the caller)
* @return <code>true</code> if the specified objects are equal.
*/
public boolean equals(Object o1, Object o2, String where) {
if (o1 == o2)
return true;
if (o1 == null) {
logUnequal(o1, o2, where + msgMeNull);
return false;
}
if (o2 == null) {
logUnequal(o1, o2, where + msgOtherNull);
return false;
}
if (!o1.equals(o2)) {
logUnequal(o1, o2, where);
return false;
}
return true;
}
/** Returns <code>true</code>, if compare called for the specified
* BigDecimal objects returns <code>0</code>. Please note, two
* BigDecimal instances are not equal (using equals) if their scale
* differs, and this method compares the values, ignoring scale.
* @param o1 one object to be tested for equality
* @param o2 the other object to be tested for equality
* @param where the location of the inequality (provided by the caller)
* @return <code>true</code> if the specified BigDecimal objects are
* equal.
*/
public boolean equals(BigDecimal o1, BigDecimal o2, String where) {
if (o1 == o2)
return true;
if ((o1 == null) || (o2 == null)) {
logUnequal(o1, o2, where);
return false;
}
boolean result = o1.equals(o2);
if (!result)
logUnequal(o1, o2, where);
return result;
}
/** Returns <code>true</code>, if two parameters are equal.
* @param p1 one to be tested for equality
* @param p2 the other to be tested for equality
* @param where the location of the inequality (provided by the caller)
* @return <code>true</code> if the parameters are equal.
*/
public boolean equals(boolean p1, boolean p2, String where) {
if (p1 != p2) {
logUnequal(Boolean.toString(p1), Boolean.toString(p2), where);
return false;
}
return true;
}
/** Returns <code>true</code>, if two parameters are equal.
* @param p1 one to be tested for equality
* @param p2 the other to be tested for equality
* @param where the location of the inequality (provided by the caller)
* @return <code>true</code> if the parameters are equal.
*/
public boolean equals(char p1, char p2, String where) {
if (p1 != p2) {
logUnequal(Character.toString(p1), Character.toString(p2), where);
return false;
}
return true;
}
/** Returns <code>true</code>, if two parameters are equal.
* @param p1 one to be tested for equality
* @param p2 the other to be tested for equality
* @param where the location of the inequality (provided by the caller)
* @return <code>true</code> if the parameters are equal.
*/
public boolean equals(byte p1, byte p2, String where) {
if (p1 != p2) {
logUnequal(Byte.toString(p1), Byte.toString(p2), where);
return false;
}
return true;
}
/** Returns <code>true</code>, if two parameters are equal.
* @param p1 one to be tested for equality
* @param p2 the other to be tested for equality
* @param where the location of the inequality (provided by the caller)
* @return <code>true</code> if the parameters are equal.
*/
public boolean equals(short p1, short p2, String where) {
if (p1 != p2) {
logUnequal(Short.toString(p1), Short.toString(p2), where);
return false;
}
return true;
}
/** Returns <code>true</code>, if two parameters are equal.
* @param p1 one to be tested for equality
* @param p2 the other to be tested for equality
* @param where the location of the inequality (provided by the caller)
* @return <code>true</code> if the parameters are equal.
*/
public boolean equals(int p1, int p2, String where) {
if (p1 != p2) {
logUnequal(Integer.toString(p1), Integer.toString(p2), where);
return false;
}
return true;
}
/** Returns <code>true</code>, if two parameters are equal.
* @param p1 one to be tested for equality
* @param p2 the other to be tested for equality
* @param where the location of the inequality (provided by the caller)
* @return <code>true</code> if the parameters are equal.
*/
public boolean equals(long p1, long p2, String where) {
if (p1 != p2) {
logUnequal(Long.toString(p1), Long.toString(p2), where);
return false;
}
return true;
}
/** Returns <code>true</code> if the specified objects are equal.
* This is a helper method checking for identical and <code>null</code>
* objects before delegating to the regular equals method.
* @param o1 one object to be tested for equality
* @param o2 the other object to be tested for equality
* @return <code>true</code> if the specified objects are equal.
*/
public static boolean equals(Object o1, Object o2) {
if (o1 == o2) {
return true;
}
if (o1 == null || o2 == null) {
return false;
}
return o1.equals(o2);
}
// Methods to support "close enough" comparison
/** Returns <code>true</code> if the specified objects are close
* enough to be considered to be equal for a deep equals
* comparison. The method delegates to the method taking double
* or float values if the specified objects are Float or Double
* wrappers. Otherwise it delegates to equals.
* @param o1 one object to be tested for close enough
* @param o2 the other object to be tested for close enough
* @param where the location of the inequality (provided by the caller)
* @return <code>true</code> if the specified values are close enough.
*/
public boolean closeEnough(Object o1, Object o2, String where) {
if (o1 == o2)
return true;
if ((o1 == null) || (o2 == null)) {
logUnequal(o1, o2, where);
return false;
}
boolean result = true;
if ((o1 instanceof Double) && (o2 instanceof Double)) {
return closeEnough(((Double)o1).doubleValue(),
((Double)o2).doubleValue(), where);
}
else if ((o1 instanceof Float) && (o2 instanceof Float)) {
return closeEnough(((Float)o1).floatValue(),
((Float)o2).floatValue(), where);
}
else if ((o1 instanceof BigDecimal) && (o2 instanceof BigDecimal)) {
return ((BigDecimal)o1).compareTo((BigDecimal)o2) == 0;
}
else {
result = o1.equals(o2);
}
if (!result)
logUnequal(o1, o2, where);
return result;
}
/** Returns <code>true</code> if the specified float values are close
* enough to be considered to be equal for a deep equals
* comparison. Floating point values are not exact, so comparing them
* using <code>==</code> might not return useful results. This method
* checks that both double values are within some percent of each
* other.
* @param d1 one double to be tested for close enough
* @param d2 the other double to be tested for close enough
* @param where the location of the inequality (provided by the caller)
* @return <code>true</code> if the specified values are close enough.
*/
public boolean closeEnough(double d1, double d2, String where) {
if (d1 == d2)
return true;
double diff = Math.abs(d1 - d2);
boolean result = diff < Math.abs((d1 + d2) * DOUBLE_EPSILON);
if (!result)
logUnequal(Double.toString(d1), Double.toString(d2), where);
return result;
}
/**
* Returns <code>true</code> if the specified float values are close
* enough to be considered to be equal for a deep equals
* comparison. Floating point values are not exact, so comparing them
* using <code>==</code> might not return useful results. This method
* checks that both float values are within some percent of each
* other.
* @param f1 one float to be tested for close enough
* @param f2 the other float to be tested for close enough
* @param where the location of the inequality (provided by the caller)
* @return <code>true</code> if the specified values are close enough.
*/
public boolean closeEnough(float f1, float f2, String where) {
if (f1 == f2)
return true;
float diff = Math.abs(f1 - f2);
boolean result = diff < Math.abs((f1 + f2) * FLOAT_EPSILON);
if (!result)
logUnequal(Float.toString(f1), Float.toString(f2), where);
return result;
}
// Methods to support compare methods as specified in Comparator
/**
* Compares its two arguments for order. Returns a negative integer, zero,
* or a positive integer as the first argument is less than, equal to, or
* greater than the second.
* @param l1 the first long to be compared
* @param l2 the second long to be compared
* @return a negative integer, zero, or a positive integer as the first
* argument is less than, equal to, or greater than the second.
*/
public static int compare (long l1, long l2) {
return (l1 < l2 ? -1 : (l1 == l2 ? 0 : 1));
}
}