/*
* Copyright Aduna (http://www.aduna-software.com/) (c) 2007.
*
* Licensed under the Aduna BSD-style license.
*/
package org.openrdf.query.algebra.evaluation.util;
import java.util.Comparator;
import info.aduna.lang.ObjectUtil;
import org.openrdf.model.BNode;
import org.openrdf.model.Literal;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import org.openrdf.model.datatypes.XMLDatatypeUtil;
import org.openrdf.query.algebra.Compare.CompareOp;
import org.openrdf.query.algebra.evaluation.ValueExprEvaluationException;
/**
* A comparator that compares values according the SPARQL value ordering as
* specified in <A
* href="http://www.w3.org/TR/rdf-sparql-query/#modOrderBy">SPARQL Query
* Language for RDF</a>.
*
* @author James Leigh
* @author Arjohn Kampman
*/
public class ValueComparator implements Comparator<Value> {
public int compare(Value o1, Value o2) {
// check equality
if (ObjectUtil.nullEquals(o1, o2)) {
return 0;
}
// 1. (Lowest) no value assigned to the variable
if (o1 == null) {
return -1;
}
if (o2 == null) {
return 1;
}
// 2. Blank nodes
boolean b1 = o1 instanceof BNode;
boolean b2 = o2 instanceof BNode;
if (b1 && b2) {
return 0;
}
if (b1) {
return -1;
}
if (b2) {
return 1;
}
// 3. IRIs
boolean u1 = o1 instanceof URI;
boolean u2 = o2 instanceof URI;
if (u1 && u2) {
return compareURIs((URI)o1, (URI)o2);
}
if (u1) {
return -1;
}
if (u2) {
return 1;
}
// 4. RDF literals
return compareLiterals((Literal)o1, (Literal)o2);
}
private int compareURIs(URI leftURI, URI rightURI) {
return leftURI.toString().compareTo(rightURI.toString());
}
private int compareLiterals(Literal leftLit, Literal rightLit) {
// Additional constraint for ORDER BY: "A plain literal is lower
// than an RDF literal with type xsd:string of the same lexical
// form."
if (!QueryEvaluationUtil.isStringLiteral(leftLit) || !QueryEvaluationUtil.isStringLiteral(rightLit)) {
try {
boolean isSmaller = QueryEvaluationUtil.compareLiterals(leftLit, rightLit, CompareOp.LT);
if (isSmaller) {
return -1;
}
else {
return 1;
}
}
catch (ValueExprEvaluationException e) {
// literals cannot be compared using the '<' operator, continue
// below
}
}
int result = 0;
// Sort by datatype first, plain literals come before datatyped literals
URI leftDatatype = leftLit.getDatatype();
URI rightDatatype = rightLit.getDatatype();
if (leftDatatype != null) {
if (rightDatatype != null) {
// Both literals have datatypes
result = compareDatatypes(leftDatatype, rightDatatype);
}
else {
result = 1;
}
}
else if (rightDatatype != null) {
result = -1;
}
if (result == 0) {
// datatypes are equal or both literals are untyped; sort by language
// tags, simple literals come before literals with language tags
String leftLanguage = leftLit.getLanguage();
String rightLanguage = rightLit.getLanguage();
if (leftLanguage != null) {
if (rightLanguage != null) {
result = leftLanguage.compareTo(rightLanguage);
}
else {
result = 1;
}
}
else if (rightLanguage != null) {
result = -1;
}
}
if (result == 0) {
// Literals are equal as fas as their datatypes and language tags are
// concerned, compare their labels
result = leftLit.getLabel().compareTo(rightLit.getLabel());
}
return result;
}
/**
* Compares two literal datatypes and indicates if one should be ordered
* after the other. This algorithm ensures that compatible ordered datatypes
* (numeric and date/time) are grouped together so that
* {@link QueryEvaluationUtil#compareLiterals(Literal, Literal, CompareOp)}
* is used in consecutive ordering steps.
*/
private int compareDatatypes(URI leftDatatype, URI rightDatatype) {
if (XMLDatatypeUtil.isNumericDatatype(leftDatatype)) {
if (XMLDatatypeUtil.isNumericDatatype(rightDatatype)) {
// both are numeric datatypes
return compareURIs(leftDatatype, rightDatatype);
}
else {
return -1;
}
}
else if (XMLDatatypeUtil.isNumericDatatype(rightDatatype)) {
return 1;
}
else if (XMLDatatypeUtil.isCalendarDatatype(leftDatatype)) {
if (XMLDatatypeUtil.isCalendarDatatype(rightDatatype)) {
// both are calendar datatypes
return compareURIs(leftDatatype, rightDatatype);
}
else {
return -1;
}
}
else if (XMLDatatypeUtil.isCalendarDatatype(rightDatatype)) {
return 1;
}
else {
// incompatible or unordered datatypes
return compareURIs(leftDatatype, rightDatatype);
}
}
}