/*******************************************************************************
* Copyright (c) 2004, 2007 IBM Corporation and Cambridge Semantics Incorporated.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* which accompanies this distribution, and is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* File: $Source: /cvsroot/slrp/glitter/com.ibm.adtech.glitter/src/com/ibm/adtech/glitter/rdf/datatype/TypedValueMapper.java,v $
* Created by: Lee Feigenbaum (<a href="mailto:feigenbl@us.ibm.com">feigenbl@us.ibm.com</a>)
* Created on: 10/23/06
* Revision: $Id: TypedValueMapper.java 164 2007-07-31 14:11:09Z mroy $
*
* Contributors: IBM Corporation - initial API and implementation
* Cambridge Semantics Incorporated - Fork to Anzo
*******************************************************************************/
package org.openanzo.rdf.datatype;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import javax.xml.datatype.DatatypeConstants;
import org.apache.commons.collections15.MultiMap;
import org.apache.commons.collections15.multimap.MultiHashMap;
import org.openanzo.rdf.URI;
import org.openanzo.rdf.utils.Pair;
/**
* The typed value mapper uses a series of {@link TypeMaps}s to convert between lexical forms of literal values and native Java values.
*
* The mapping from datatype URI to a native Java class must be a single-value function. That is, the a particular datatype URI cannot map to multiple Java
* classes. However, the reverse mapping is not so restricted. A single Java class may map to multiple datatype URIs. For example, the XMLGregorianCalendar
* class can map to xsd:date, xsd:time, xsd:gMonth, etc. based on the data in the particular instance of the class. This restriction that the URI->Class map be
* a single-valued function is in place for Jastor which must map from datatype URI to Java class for generating classes from ontologies.
*
* @author Lee
* @author Jordi Albornoz Mulligan (<a href="mailto:jordi@cambridgesemantics.com">jordi@cambridgesemantics.com </a>)
*
*/
final public class TypedValueMapper {
/**
* Converts the lexical form of a value to a native Java value using the given XML schema type to inform the conversion.
*
* @param value
* value to convert
* @param datatype
* XML schema type of value
* @return the native object value for the given XML schema type
* @throws IllegalArgumentException
*/
static public Object getNativeObject(String value, URI datatype) throws IllegalArgumentException {
Object obj = null;
LexicalToNativeTypeMap tm = xs2j.get(datatype);
if (tm != null) {
obj = tm.convertToNativeObject(value);
}
return obj;
}
/**
* Returns the Class to which values of the given datatype are converted by the {@link #getNativeObject(String, URI)} method.
*
* @param datatype
* XML schema type
* @return the Class which maps to the given XML schema type
*/
static public Class<?> getNativeClass(URI datatype) {
Class<?> clazz = null;
LexicalToNativeTypeMap tm = xs2j.get(datatype);
if (tm != null) {
clazz = tm.getOutputJavaClass();
}
return clazz;
}
/**
* Returns the lexical form of the given native value, as informed by the type of the value. And also return the XML Schema type corresponding to the class
* of the native Java object given.
*
* @param o
* native object to convert
* @return A pair consisting of a string and a URI. The first string is the lexical form of the given native value. The second element, the URI, is the XML
* Schema type corresponding to the class of the native Java object given.
*/
static public Pair<String, URI> getLexicalValue(Object o) {
return getLexicalValue(o, null);
}
/**
* @param o
* native object to convert
* @param desiredDatatype
* an optional datatype. If supplied, object will only attempt to be converted to a lexical value of the desiredDatatype. If the object can't be
* converted to that datatype, then null will be returned.
* @return A pair consisting of a string and a URI. The first string is the lexical form of the given native value. The second element, the URI, is the XML
* Schema type corresponding to the class of the native Java object given.
*/
static public Pair<String, URI> getLexicalValue(Object o, URI desiredDatatype) {
Pair<String, URI> ret = null;
NativeToLexicalTypeMap tm = getTypeMapForObject(o, desiredDatatype);
if (tm != null) {
ret = new Pair<String, URI>(tm.convertToLexicalValue(o), tm.getOutputDatatype());
}
return ret;
}
/**
* Retrieves an ITypeMap which applies to the type of the given object.
*
* @param o
* object for which a matching ITypeMap will be retrieved.
* @param desiredDatatype
* an optional datatype. If supplied, the map returned must be a map that returns a value of this datatype.
* @return a matching ITypeMap or null if none could be found.
*/
static private NativeToLexicalTypeMap getTypeMapForObject(Object o, URI desiredDatatype) {
NativeToLexicalTypeMap tm = null;
Collection<NativeToLexicalTypeMap> tmList = j2xs.get(o.getClass());
if (tmList == null) {
// We didn't find a type map using simple comparison of the Object's class.
// But the object may still apply to one of the maps via subclassing or
// interface implementation. To check that, we iterate through
// all of the type maps checking if the object is an instanceof each
// ITypeMap's supported type AND it can convert the given object. If a desiredDatatype
// was given, we also check that the type map outputs the desired datatype.
for (Map.Entry<Class<?>, Collection<NativeToLexicalTypeMap>> currentTypeMaps : j2xs.entrySet()) {
for (NativeToLexicalTypeMap currentTypeMap : currentTypeMaps.getValue()) {
Class<?> c = currentTypeMap.getJavaClass();
if (c != null && c.isInstance(o) && currentTypeMap.canConvertToLexicalValue(o) && (desiredDatatype == null || desiredDatatype.equals(currentTypeMap.getOutputDatatype()))) {
tm = currentTypeMap;
break;
}
}
}
} else {
for (NativeToLexicalTypeMap currentTypeMap : tmList) {
if (currentTypeMap.canConvertToLexicalValue(o)) {
tm = currentTypeMap;
break;
}
}
}
return tm;
}
static private final Map<URI, LexicalToNativeTypeMap> xs2j = new HashMap<URI, LexicalToNativeTypeMap>();
static private final MultiMap<Class<?>, NativeToLexicalTypeMap> j2xs = new MultiHashMap<Class<?>, NativeToLexicalTypeMap>();
static private void addLexicalToNativeMapping(LexicalToNativeTypeMap tm) {
LexicalToNativeTypeMap old = xs2j.put(tm.getDatatype(), tm);
if (old != null) {
throw new RuntimeException("Invalid type map configuration. An datatype URI must map to only one Java class. Datatype URI <" + tm.getDatatype() + "> is already mapped to " + old.getOutputJavaClass() + " via " + old.toString());
}
}
static private void addNativeToLexicalMapping(NativeToLexicalTypeMap tm) {
j2xs.put(tm.getJavaClass(), tm);
}
static {
TypeMaps.TMAnyUri anyUriMap = new TypeMaps.TMAnyUri();
addLexicalToNativeMapping(anyUriMap);
addNativeToLexicalMapping(anyUriMap);
TypeMaps.TMBase64Binary base64Map = new TypeMaps.TMBase64Binary();
addLexicalToNativeMapping(base64Map);
addNativeToLexicalMapping(base64Map);
TypeMaps.TMBoolean booleanMap = new TypeMaps.TMBoolean();
addLexicalToNativeMapping(booleanMap);
addNativeToLexicalMapping(booleanMap);
TypeMaps.TMByte byteMap = new TypeMaps.TMByte();
addLexicalToNativeMapping(byteMap);
addNativeToLexicalMapping(byteMap);
TypeMaps.TMDecimal decimalMap = new TypeMaps.TMDecimal();
addLexicalToNativeMapping(decimalMap);
addNativeToLexicalMapping(decimalMap);
TypeMaps.TMDouble doubleMap = new TypeMaps.TMDouble();
addLexicalToNativeMapping(doubleMap);
addNativeToLexicalMapping(doubleMap);
TypeMaps.TMFloat floatMap = new TypeMaps.TMFloat();
addLexicalToNativeMapping(floatMap);
addNativeToLexicalMapping(floatMap);
addLexicalToNativeMapping(new TypeMaps.TMHexBinary());
TypeMaps.TMInt intMap = new TypeMaps.TMInt();
addLexicalToNativeMapping(intMap);
addNativeToLexicalMapping(intMap);
TypeMaps.TMInteger integerMap = new TypeMaps.TMInteger();
addLexicalToNativeMapping(integerMap);
addNativeToLexicalMapping(integerMap);
TypeMaps.TMLong longMap = new TypeMaps.TMLong();
addLexicalToNativeMapping(longMap);
addNativeToLexicalMapping(longMap);
TypeMaps.TMShort shortMap = new TypeMaps.TMShort();
addLexicalToNativeMapping(shortMap);
addNativeToLexicalMapping(shortMap);
TypeMaps.TMString stringMap = new TypeMaps.TMString();
addLexicalToNativeMapping(stringMap);
addNativeToLexicalMapping(stringMap);
addLexicalToNativeMapping(new TypeMaps.TMUnsignedInt());
addLexicalToNativeMapping(new TypeMaps.TMUnsignedShort());
addLexicalToNativeMapping(new TypeMaps.TMUnsignedByte());
addNativeToLexicalMapping(new TypeMaps.TMJavaDate());
addNativeToLexicalMapping(new TypeMaps.TMJavaCalendar());
addNativeToLexicalMapping(new TypeMaps.TMSQLTimestamp());
addNativeToLexicalMapping(new TypeMaps.TMSQLTime());
addNativeToLexicalMapping(new TypeMaps.TMSQLDate());
TypeMaps.TMXMLGregorianCalendar datetimeMap = new TypeMaps.TMXMLGregorianCalendar(DatatypeConstants.DATETIME);
addLexicalToNativeMapping(datetimeMap);
addNativeToLexicalMapping(datetimeMap);
TypeMaps.TMXMLGregorianCalendar dateMap = new TypeMaps.TMXMLGregorianCalendar(DatatypeConstants.DATE);
addLexicalToNativeMapping(dateMap);
addNativeToLexicalMapping(dateMap);
TypeMaps.TMXMLGregorianCalendar timeMap = new TypeMaps.TMXMLGregorianCalendar(DatatypeConstants.TIME);
addLexicalToNativeMapping(timeMap);
addNativeToLexicalMapping(timeMap);
TypeMaps.TMXMLGregorianCalendar gdayMap = new TypeMaps.TMXMLGregorianCalendar(DatatypeConstants.GDAY);
addLexicalToNativeMapping(gdayMap);
addNativeToLexicalMapping(gdayMap);
TypeMaps.TMXMLGregorianCalendar gmonthMap = new TypeMaps.TMXMLGregorianCalendar(DatatypeConstants.GMONTH);
addLexicalToNativeMapping(gmonthMap);
addNativeToLexicalMapping(gmonthMap);
TypeMaps.TMXMLGregorianCalendar gmonthdayMap = new TypeMaps.TMXMLGregorianCalendar(DatatypeConstants.GMONTHDAY);
addLexicalToNativeMapping(gmonthdayMap);
addNativeToLexicalMapping(gmonthdayMap);
TypeMaps.TMXMLGregorianCalendar gyearMap = new TypeMaps.TMXMLGregorianCalendar(DatatypeConstants.GYEAR);
addLexicalToNativeMapping(gyearMap);
addNativeToLexicalMapping(gyearMap);
TypeMaps.TMXMLGregorianCalendar gyearmonthMap = new TypeMaps.TMXMLGregorianCalendar(DatatypeConstants.GYEARMONTH);
addLexicalToNativeMapping(gyearmonthMap);
addNativeToLexicalMapping(gyearmonthMap);
TypeMaps.TMDuration durationMap = new TypeMaps.TMDuration(DatatypeConstants.DURATION);
addLexicalToNativeMapping(durationMap);
addNativeToLexicalMapping(durationMap);
TypeMaps.TMDuration durationDayTimeMap = new TypeMaps.TMDuration(DatatypeConstants.DURATION_DAYTIME);
addLexicalToNativeMapping(durationDayTimeMap);
addNativeToLexicalMapping(durationDayTimeMap);
TypeMaps.TMDuration durationYearMonthMap = new TypeMaps.TMDuration(DatatypeConstants.DURATION_YEARMONTH);
addLexicalToNativeMapping(durationYearMonthMap);
addNativeToLexicalMapping(durationYearMonthMap);
addLexicalToNativeMapping(new TypeMaps.TMXmlLiteralString());
// TODO: what about positiveInteger, etc.?
}
}