/* Copyright 2008, 2009, 2010 by the Oxford University Computing Laboratory
This file is part of HermiT.
HermiT 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.
HermiT 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 HermiT. If not, see <http://www.gnu.org/licenses/>.
*/
package org.semanticweb.HermiT.datatypes.owlreal;
import java.math.BigInteger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.semanticweb.HermiT.Prefixes;
import org.semanticweb.HermiT.datatypes.DatatypeHandler;
import org.semanticweb.HermiT.datatypes.MalformedLiteralException;
import org.semanticweb.HermiT.datatypes.UnsupportedFacetException;
import org.semanticweb.HermiT.datatypes.ValueSpaceSubset;
import org.semanticweb.HermiT.model.Constant;
import org.semanticweb.HermiT.model.DatatypeRestriction;
/**
* Implements a handler for the numeric datatypes derived from owl:real. This class
* makes am important assumption that all data values are represented with objects
* whose .equals() method behaves as expected. This means that, for numbers represented
* using Java's classes, one needs to use the most specific class. For example, a decimal
* 1.0 *must* be represented as Integer 1, and a rational 7/2 *must* be represented
* as BigDecimal 3.5.
*/
public class OWLRealDatatypeHandler implements DatatypeHandler {
protected static final String OWL_NS=Prefixes.s_semanticWebPrefixes.get("owl");
protected static final String XSD_NS=Prefixes.s_semanticWebPrefixes.get("xsd");
protected static final Map<String,NumberInterval> s_intervalsByDatatype=new HashMap<String,NumberInterval>();
protected static final Map<String,ValueSpaceSubset> s_subsetsByDatatype=new HashMap<String,ValueSpaceSubset>();
static {
Object[][] initializer=new Object[][] {
{ OWL_NS+"real", NumberRange.REAL, MinusInfinity.INSTANCE,BoundType.EXCLUSIVE,PlusInfinity.INSTANCE, BoundType.EXCLUSIVE },
{ OWL_NS+"rational", NumberRange.RATIONAL,MinusInfinity.INSTANCE,BoundType.EXCLUSIVE,PlusInfinity.INSTANCE, BoundType.EXCLUSIVE },
{ XSD_NS+"decimal", NumberRange.DECIMAL, MinusInfinity.INSTANCE,BoundType.EXCLUSIVE,PlusInfinity.INSTANCE, BoundType.EXCLUSIVE },
{ XSD_NS+"integer", NumberRange.INTEGER, MinusInfinity.INSTANCE,BoundType.EXCLUSIVE,PlusInfinity.INSTANCE, BoundType.EXCLUSIVE },
{ XSD_NS+"nonNegativeInteger",NumberRange.INTEGER, Integer.valueOf(0), BoundType.INCLUSIVE,PlusInfinity.INSTANCE, BoundType.EXCLUSIVE },
{ XSD_NS+"positiveInteger", NumberRange.INTEGER, Integer.valueOf(0), BoundType.EXCLUSIVE,PlusInfinity.INSTANCE, BoundType.EXCLUSIVE },
{ XSD_NS+"nonPositiveInteger",NumberRange.INTEGER, MinusInfinity.INSTANCE,BoundType.EXCLUSIVE,Integer.valueOf(0), BoundType.INCLUSIVE },
{ XSD_NS+"negativeInteger", NumberRange.INTEGER, MinusInfinity.INSTANCE,BoundType.EXCLUSIVE,Integer.valueOf(0), BoundType.EXCLUSIVE },
{ XSD_NS+"long", NumberRange.INTEGER, Long.MIN_VALUE, BoundType.INCLUSIVE,Long.MAX_VALUE, BoundType.INCLUSIVE },
{ XSD_NS+"int", NumberRange.INTEGER, Integer.MIN_VALUE, BoundType.INCLUSIVE,Integer.MAX_VALUE, BoundType.INCLUSIVE },
{ XSD_NS+"short", NumberRange.INTEGER, (int)Short.MIN_VALUE, BoundType.INCLUSIVE,(int)Short.MAX_VALUE, BoundType.INCLUSIVE },
{ XSD_NS+"byte", NumberRange.INTEGER, (int)Byte.MIN_VALUE, BoundType.INCLUSIVE,(int)Byte.MAX_VALUE, BoundType.INCLUSIVE },
{ XSD_NS+"unsignedLong", NumberRange.INTEGER, Integer.valueOf(0), BoundType.INCLUSIVE,new BigInteger("18446744073709551615"), BoundType.INCLUSIVE },
{ XSD_NS+"unsignedInt", NumberRange.INTEGER, Integer.valueOf(0), BoundType.INCLUSIVE,4294967295L, BoundType.INCLUSIVE },
{ XSD_NS+"unsignedShort", NumberRange.INTEGER, Integer.valueOf(0), BoundType.INCLUSIVE,65535, BoundType.INCLUSIVE },
{ XSD_NS+"unsignedByte", NumberRange.INTEGER, Integer.valueOf(0), BoundType.INCLUSIVE,255, BoundType.INCLUSIVE },
};
for (Object[] row : initializer) {
String datatypeURI=(String)row[0];
NumberInterval interval=new NumberInterval((NumberRange)row[1],NumberRange.NOTHING,(Number)row[2],(BoundType)row[3],(Number)row[4],(BoundType)row[5]);
s_intervalsByDatatype.put(datatypeURI,interval);
s_subsetsByDatatype.put(datatypeURI,new OWLRealValueSpaceSubset(interval));
}
}
protected static final ValueSpaceSubset EMPTY_SUBSET=new OWLRealValueSpaceSubset();
protected static final Set<String> s_supportedFacetURIs=new HashSet<String>();
static {
s_supportedFacetURIs.add(XSD_NS+"minInclusive");
s_supportedFacetURIs.add(XSD_NS+"minExclusive");
s_supportedFacetURIs.add(XSD_NS+"maxInclusive");
s_supportedFacetURIs.add(XSD_NS+"maxExclusive");
}
protected static final Map<String,Set<String>> s_datatypeSupersets=new HashMap<String,Set<String>>();
protected static final Map<String,Set<String>> s_datatypeDisjoints=new HashMap<String,Set<String>>();
static {
for (String datatypeURI : s_intervalsByDatatype.keySet()) {
s_datatypeSupersets.put(datatypeURI,new HashSet<String>());
s_datatypeDisjoints.put(datatypeURI,new HashSet<String>());
}
for (Map.Entry<String,NumberInterval> entry1 : s_intervalsByDatatype.entrySet()) {
String datatypeURI1=entry1.getKey();
NumberInterval interval1=entry1.getValue();
for (Map.Entry<String,NumberInterval> entry2 : s_intervalsByDatatype.entrySet()) {
String datatypeURI2=entry2.getKey();
NumberInterval interval2=entry2.getValue();
NumberInterval intersection=interval1.intersectWith(interval2);
if (intersection==null)
s_datatypeDisjoints.get(datatypeURI1).add(datatypeURI2);
else if (intersection==interval1) {
// The above test depends on the fact that NumberInterval.intersectWith() will not
// create a new interval object if interval1 is contained in interval2.
s_datatypeSupersets.get(datatypeURI1).add(datatypeURI2);
}
}
}
}
public Set<String> getManagedDatatypeURIs() {
return s_intervalsByDatatype.keySet();
}
public Object parseLiteral(String lexicalForm,String datatypeURI) throws MalformedLiteralException {
assert s_intervalsByDatatype.keySet().contains(datatypeURI);
try {
if ((OWL_NS+"real").equals(datatypeURI))
throw new MalformedLiteralException(lexicalForm,datatypeURI);
else if ((OWL_NS+"rational").equals(datatypeURI))
return Numbers.parseRational(lexicalForm);
else if ((XSD_NS+"decimal").equals(datatypeURI))
return Numbers.parseDecimal(lexicalForm);
else
return Numbers.parseInteger(lexicalForm);
}
catch (NumberFormatException error) {
throw new MalformedLiteralException(lexicalForm,datatypeURI);
}
}
public void validateDatatypeRestriction(DatatypeRestriction datatypeRestriction) throws UnsupportedFacetException {
assert s_intervalsByDatatype.keySet().contains(datatypeRestriction.getDatatypeURI());
for (int index=datatypeRestriction.getNumberOfFacetRestrictions()-1;index>=0;--index) {
String facetURI=datatypeRestriction.getFacetURI(index);
if (!s_supportedFacetURIs.contains(facetURI))
throw new UnsupportedFacetException("Facet with URI '"+facetURI+"' is not supported on datatypes derived from owl:real.");
Constant facetValue=datatypeRestriction.getFacetValue(index);
Object facetDataValue=facetValue.getDataValue();
if (!(facetDataValue instanceof Number))
throw new UnsupportedFacetException("Facet with URI '"+facetURI+"' takes only numbers as values.");
if (!Numbers.isValidNumber((Number)facetDataValue))
throw new UnsupportedFacetException("Facet with URI '"+facetURI+"' does not support '"+facetValue.toString()+"' as value.");
}
}
public ValueSpaceSubset createValueSpaceSubset(DatatypeRestriction datatypeRestriction) {
assert s_intervalsByDatatype.keySet().contains(datatypeRestriction.getDatatypeURI());
if (datatypeRestriction.getNumberOfFacetRestrictions()==0)
return s_subsetsByDatatype.get(datatypeRestriction.getDatatypeURI());
NumberInterval interval=getIntervalFor(datatypeRestriction);
if (interval==null)
return EMPTY_SUBSET;
else
return new OWLRealValueSpaceSubset(interval);
}
public ValueSpaceSubset conjoinWithDR(ValueSpaceSubset valueSpaceSubset,DatatypeRestriction datatypeRestriction) {
assert s_intervalsByDatatype.keySet().contains(datatypeRestriction.getDatatypeURI());
NumberInterval interval=getIntervalFor(datatypeRestriction);
if (interval==null)
return EMPTY_SUBSET;
else {
OWLRealValueSpaceSubset realSubset=(OWLRealValueSpaceSubset)valueSpaceSubset;
List<NumberInterval> oldIntervals=realSubset.m_intervals;
List<NumberInterval> newIntervals=new ArrayList<NumberInterval>();
for (int index=0;index<oldIntervals.size();index++) {
NumberInterval oldInterval=oldIntervals.get(index);
NumberInterval intersection=oldInterval.intersectWith(interval);
if (intersection!=null)
newIntervals.add(intersection);
}
if (newIntervals.isEmpty())
return EMPTY_SUBSET;
else
return new OWLRealValueSpaceSubset(newIntervals);
}
}
public ValueSpaceSubset conjoinWithDRNegation(ValueSpaceSubset valueSpaceSubset,DatatypeRestriction datatypeRestriction) {
assert s_intervalsByDatatype.keySet().contains(datatypeRestriction.getDatatypeURI());
NumberInterval interval=getIntervalFor(datatypeRestriction);
if (interval==null)
return valueSpaceSubset;
else {
NumberInterval complementInterval1=null;
if (!interval.m_lowerBound.equals(MinusInfinity.INSTANCE))
complementInterval1=new NumberInterval(NumberRange.REAL,NumberRange.NOTHING,MinusInfinity.INSTANCE,BoundType.EXCLUSIVE,interval.m_lowerBound,interval.m_lowerBoundType.getComplement());
NumberInterval complementInterval2=null;
if (!interval.m_baseRange.equals(NumberRange.REAL))
complementInterval2=new NumberInterval(NumberRange.REAL,interval.m_baseRange,interval.m_lowerBound,interval.m_lowerBoundType,interval.m_upperBound,interval.m_upperBoundType);
NumberInterval complementInterval3=null;
if (!interval.m_upperBound.equals(PlusInfinity.INSTANCE))
complementInterval3=new NumberInterval(NumberRange.REAL,NumberRange.NOTHING,interval.m_upperBound,interval.m_upperBoundType.getComplement(),PlusInfinity.INSTANCE,BoundType.EXCLUSIVE);
OWLRealValueSpaceSubset realSubset=(OWLRealValueSpaceSubset)valueSpaceSubset;
List<NumberInterval> oldIntervals=realSubset.m_intervals;
List<NumberInterval> newIntervals=new ArrayList<NumberInterval>();
for (int index=0;index<oldIntervals.size();index++) {
NumberInterval oldInterval=oldIntervals.get(index);
if (complementInterval1!=null) {
NumberInterval intersection=oldInterval.intersectWith(complementInterval1);
if (intersection!=null)
newIntervals.add(intersection);
}
if (complementInterval2!=null) {
NumberInterval intersection=oldInterval.intersectWith(complementInterval2);
if (intersection!=null)
newIntervals.add(intersection);
}
if (complementInterval3!=null) {
NumberInterval intersection=oldInterval.intersectWith(complementInterval3);
if (intersection!=null)
newIntervals.add(intersection);
}
}
if (newIntervals.isEmpty())
return EMPTY_SUBSET;
else
return new OWLRealValueSpaceSubset(newIntervals);
}
}
protected NumberInterval getIntervalFor(DatatypeRestriction datatypeRestriction) {
NumberInterval baseInterval=s_intervalsByDatatype.get(datatypeRestriction.getDatatypeURI());
if (datatypeRestriction.getNumberOfFacetRestrictions()==0)
return baseInterval;
NumberRange baseRange=baseInterval.m_baseRange;
NumberRange excludedRange=baseInterval.m_excludedRange;
Number lowerBound=baseInterval.m_lowerBound;
BoundType lowerBoundType=baseInterval.m_lowerBoundType;
Number upperBound=baseInterval.m_upperBound;
BoundType upperBoundType=baseInterval.m_upperBoundType;
for (int index=datatypeRestriction.getNumberOfFacetRestrictions()-1;index>=0;--index) {
String facetURI=datatypeRestriction.getFacetURI(index);
Number facetDataValue=(Number)datatypeRestriction.getFacetValue(index).getDataValue();
if ((XSD_NS+"minInclusive").equals(facetURI)) {
int comparison=Numbers.compare(facetDataValue,lowerBound);
if (comparison>0) {
lowerBound=facetDataValue;
lowerBoundType=BoundType.INCLUSIVE;
}
// If the numbers are equal, nothing needs to be done to the bound type because
// the existing one is at least as restrictive as INCLUSIVE.
}
else if ((XSD_NS+"minExclusive").equals(facetURI)) {
int comparison=Numbers.compare(facetDataValue,lowerBound);
if (comparison>0) {
lowerBound=facetDataValue;
lowerBoundType=BoundType.EXCLUSIVE;
}
else if (comparison==0) {
// EXCLUSIVE is guaranteed to be the more restrictive bound.
lowerBoundType=BoundType.EXCLUSIVE;
}
}
else if ((XSD_NS+"maxInclusive").equals(facetURI)) {
int comparison=Numbers.compare(facetDataValue,upperBound);
if (comparison<0) {
upperBound=facetDataValue;
upperBoundType=BoundType.INCLUSIVE;
}
// If the numbers are equal, nothing needs to be done to the bound type because
// the existing one is at least as restrictive as INCLUSIVE.
}
else if ((XSD_NS+"maxExclusive").equals(facetURI)) {
int comparison=Numbers.compare(facetDataValue,upperBound);
if (comparison<0) {
upperBound=facetDataValue;
upperBoundType=BoundType.EXCLUSIVE;
}
else if (comparison==0) {
// EXCLUSIVE is guaranteed to be the more restrictive bound.
upperBoundType=BoundType.EXCLUSIVE;
}
}
else
throw new IllegalStateException("Internal error: facet '"+facetURI+"' is not supported by owl:real.");
}
if (NumberInterval.isIntervalEmpty(baseRange,excludedRange,lowerBound,lowerBoundType,upperBound,upperBoundType))
return null;
else
return new NumberInterval(baseRange,excludedRange,lowerBound,lowerBoundType,upperBound,upperBoundType);
}
public boolean isSubsetOf(String subsetDatatypeURI,String supersetDatatypeURI) {
assert s_intervalsByDatatype.keySet().contains(subsetDatatypeURI);
assert s_intervalsByDatatype.keySet().contains(supersetDatatypeURI);
return s_datatypeSupersets.get(subsetDatatypeURI).contains(supersetDatatypeURI);
}
public boolean isDisjointWith(String datatypeURI1,String datatypeURI2) {
assert s_intervalsByDatatype.keySet().contains(datatypeURI1);
assert s_intervalsByDatatype.keySet().contains(datatypeURI2);
return s_datatypeDisjoints.get(datatypeURI1).contains(datatypeURI2);
}
}