/*
* Copyright 2006 the original author or authors.
*
* Licensed 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 jdave.contract;
import java.util.Comparator;
import jdave.ExpectationFailedException;
import jdave.IContract;
/**
* A contract which enforces the contract between equals method and Comparable
* interface (or Comparator interface). See the javadoc of those classes for
* explanation of the contract. Example usage: <blockquote>
*
* <pre>
* <code>
* Integer obj = 5;
* specify(obj, should.satisfy(new EqualsComparableContract<Integer>() {
* public Integer preceding() {
* return 4;
* }
* public Integer subsequent() {
* return 6;
* }
* public Integer equivalentByComparisonButNotByEqual() {
* // There's no Integer which would be non equal with 5 and still be equal
* // by comparison (compareTo() == 0). So return null.
* return null;
* }
* });
* </code>
* </pre>
*
* </blockquote>
*
* @author Joni Freeman
*/
public abstract class EqualsComparableContract<T> implements IContract {
private Comparator<T> comparator;
public EqualsComparableContract() {
}
public EqualsComparableContract(final Comparator<T> comparator) {
this.comparator = comparator;
}
@SuppressWarnings("unchecked")
public void isSatisfied(final Object obj) throws ExpectationFailedException {
final T object = (T) obj;
if (comparator == null) {
comparator = new ComparableComparator();
try {
((Comparable<?>) obj).compareTo(null);
throw new ExpectationFailedException(object.getClass().getName()
+ ".compareTo(null) should throw NullpointerException");
} catch (final NullPointerException e) {
}
}
if (comparator.compare(object, object) != 0) {
throw new ExpectationFailedException("comparison should be 0, if objects are equal");
}
if (comparator.compare(object, preceding()) <= 0) {
throw new ExpectationFailedException(object + " should be after " + preceding());
}
if (comparator.compare(object, subsequent()) >= 0) {
throw new ExpectationFailedException(object + " should be before " + subsequent());
}
if (equivalentByComparisonButNotByEqual() != null) {
if (comparator.compare(object, equivalentByComparisonButNotByEqual()) == 0) {
throw new ExpectationFailedException("comparison is not consistent with equals, "
+ object + ", " + subsequent());
}
}
}
/**
* Return an instance which should preceed the given instance.
*
* @see #isSatisfied(Object)
*/
protected abstract T preceding();
/**
* Return an instance which should be after the given instance.
*
* @see #isSatisfied(Object)
*/
protected abstract T subsequent();
/**
* Return an instance whose compareTo is 0 with given instance, but whose
* equal returns false. Return null if no such instance exists.
*
* @see #isSatisfied(Object)
*/
protected abstract T equivalentByComparisonButNotByEqual();
private static class ComparableComparator<T extends Comparable<Object>> implements
Comparator<T> {
public int compare(final T o1, final T o2) {
return o1.compareTo(o2);
}
}
}