/* * 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 * @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); } } }