ObjectComparator.java
/*******************************************************************************
* Copyright (c) 2013 Stephen F. Siegel, University of Delaware.
*
* This file is part of SARL.
*
* SARL is free software: you can redistribute it and/or modify it under the
* terms of the GNU Lesser General Public License as published by the Free
* Software Foundation, either version 3 of the License, or (at your option) any
* later version.
*
* SARL is distributed in the hope that it will be useful, but WITHOUT ANY
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
* A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
* details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with SARL. If not, see <http://www.gnu.org/licenses/>.
******************************************************************************/
package edu.udel.cis.vsl.sarl.object.common;
import java.util.Comparator;
import edu.udel.cis.vsl.sarl.IF.SARLInternalException;
import edu.udel.cis.vsl.sarl.IF.expr.SymbolicExpression;
import edu.udel.cis.vsl.sarl.IF.number.IntegerNumber;
import edu.udel.cis.vsl.sarl.IF.number.Number;
import edu.udel.cis.vsl.sarl.IF.number.NumberFactory;
import edu.udel.cis.vsl.sarl.IF.object.BooleanObject;
import edu.udel.cis.vsl.sarl.IF.object.CharObject;
import edu.udel.cis.vsl.sarl.IF.object.IntObject;
import edu.udel.cis.vsl.sarl.IF.object.NumberObject;
import edu.udel.cis.vsl.sarl.IF.object.StringObject;
import edu.udel.cis.vsl.sarl.IF.object.SymbolicObject;
import edu.udel.cis.vsl.sarl.IF.object.SymbolicObject.SymbolicObjectKind;
import edu.udel.cis.vsl.sarl.IF.object.SymbolicSequence;
import edu.udel.cis.vsl.sarl.IF.type.SymbolicType;
import edu.udel.cis.vsl.sarl.IF.type.SymbolicTypeSequence;
/**
* The default {@link Comparator} on all {@link SymbolicObject}s. It requires
* {@link Comparator}s for {@link SymbolicExpression}s, {@link SymbolicType}s,
* and {@link SymbolicTypeSequence}s. It harness together all of these other
* comparators in order to form a more general comparator on all symbolic
* objects.
*
* @author siegel
*/
public class ObjectComparator implements Comparator<SymbolicObject> {
/**
* The {@link Comparator} on {@link SymbolicExpression}s used by this
* {@link Comparator}.
*/
private Comparator<SymbolicExpression> expressionComparator;
/**
* The factory used to perform exact, unbounded arithmetic on integer and
* real numbers.
*/
private NumberFactory numberFactory;
/**
* The {@link Comparator} on {@link SymbolicType}s used by this
* {@link Comparator}.
*/
private Comparator<SymbolicType> typeComparator;
/**
* The {@link Comparator} on {@link SymbolicTypeSequence}s used by this
* {@link Comparator}.
*/
private Comparator<SymbolicTypeSequence> typeSequenceComparator;
/**
* Creates a new instance using the given number factory. The specialized
* comparators are initially <code>null</code>. They must be set later using
* the setter methods provided here before the first comparison is
* performed.
*
* @param numberFactory
* the factory used to perform exact, unbounded real and integer
* arithmetic
*/
public ObjectComparator(NumberFactory numberFactory) {
this.numberFactory = numberFactory;
}
private int compareSequences(SymbolicSequence<?> seq1,
SymbolicSequence<?> seq2) {
int size = seq1.size();
int result = size - seq2.size();
if (result != 0)
return result;
for (int i = 0; i < size; i++) {
result = expressionComparator.compare(seq1.get(i), seq2.get(i));
if (result != 0)
return result;
}
return 0;
}
/**
* Sets the expression comparator for this object.
*
* @param c
* the expression comparator to be used by this object
*/
public void setExpressionComparator(Comparator<SymbolicExpression> c) {
expressionComparator = c;
}
/**
* Sets the type comparator for this object.
*
* @param c
* the type comparator to be used by this object
*/
public void setTypeComparator(Comparator<SymbolicType> c) {
typeComparator = c;
}
/**
* Sets the type sequence comparator for this object.
*
* @param c
* the type sequence comparator to be used by this object
*/
public void setTypeSequenceComparator(Comparator<SymbolicTypeSequence> c) {
typeSequenceComparator = c;
}
/**
* Gets the expression comparator.
*
* @return this object's expression comparator
*/
public Comparator<SymbolicExpression> expressionComparator() {
return expressionComparator;
}
/**
* Gets the type comparator.
*
* @return the object's type comparator
*/
public Comparator<SymbolicType> typeComparator() {
return typeComparator;
}
/**
* Gets the type sequence comparator.
*
* @return the object's type sequence comparator
*/
public Comparator<SymbolicTypeSequence> typeSequenceComparator() {
return typeSequenceComparator;
}
/**
* {@inheritDoc}
*
* Compares any two non-<code>null</code> {@link SymbolicObject}s. The
* objects are ordered first by "kind".
*
* @param o1
* first object
* @param o2
* second object
* @return 0 a negative integer if the first object precedes the second in
* the total order; 0 if the two objects are equal; a positive
* integer if the second object precedes the first
*/
@Override
public int compare(SymbolicObject o1, SymbolicObject o2) {
if (o1 == o2)
return 0;
SymbolicObjectKind kind = o1.symbolicObjectKind();
int result = kind.compareTo(o2.symbolicObjectKind());
if (result != 0)
return result;
switch (kind) {
case EXPRESSION:
return expressionComparator.compare((SymbolicExpression) o1,
(SymbolicExpression) o2);
case SEQUENCE:
return compareSequences((SymbolicSequence<?>) o1,
(SymbolicSequence<?>) o2);
case TYPE:
return typeComparator.compare((SymbolicType) o1, (SymbolicType) o2);
case TYPE_SEQUENCE:
return typeSequenceComparator.compare((SymbolicTypeSequence) o1,
(SymbolicTypeSequence) o2);
case BOOLEAN:
return ((BooleanObject) o1).getBoolean()
? (((BooleanObject) o2).getBoolean() ? 0 : 1)
: (((BooleanObject) o2).getBoolean() ? -1 : 0);
case INT:
return ((IntObject) o1).getInt() - ((IntObject) o2).getInt();
case NUMBER: {
Number num1 = ((NumberObject) o1).getNumber(),
num2 = ((NumberObject) o2).getNumber();
boolean isInt1 = num1 instanceof IntegerNumber,
isInt2 = num2 instanceof IntegerNumber;
if (isInt1 && !isInt2)
return 1;
if ((!isInt1) && isInt2)
return -1;
return numberFactory.compare(num1, num2);
}
case STRING:
return ((StringObject) o1).getString()
.compareTo(((StringObject) o2).getString());
case CHAR:
return Character.compare(((CharObject) o1).getChar(),
((CharObject) o2).getChar());
default:
throw new SARLInternalException(
"unreachable: unknown object kind: " + kind);
}
}
}