/*
* Copyright Aduna (http://www.aduna-software.com/) (c) 2008.
*
* Licensed under the Aduna BSD-style license.
*/
package org.openrdf.sail.rdbms.schema;
import static org.openrdf.model.datatypes.XMLDatatypeUtil.isCalendarDatatype;
import static org.openrdf.model.datatypes.XMLDatatypeUtil.isNumericDatatype;
import java.io.UnsupportedEncodingException;
import java.math.BigInteger;
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.sql.SQLException;
import org.openrdf.model.BNode;
import org.openrdf.model.Literal;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import org.openrdf.model.vocabulary.RDF;
/**
* @author James Leigh
*/
public abstract class IdSequence {
private static final String UTF_8 = "UTF-8";
private static ThreadLocal<MessageDigest> md5 = new ThreadLocal<MessageDigest>() {
@Override
protected MessageDigest initialValue() {
try {
return MessageDigest.getInstance("MD5");
}
catch (NoSuchAlgorithmException e) {
throw new AssertionError(e);
}
}
};
/** 255 */
private int LONG = 255;
private int MOD = 16;
private HashTable table;
public int getMod() {
return MOD;
}
public abstract int getShift();
public abstract int getJdbcIdType();
public abstract String getSqlType();
public HashTable getHashTable() {
return table;
}
public void setHashTable(HashTable table) {
this.table = table;
}
public abstract void init()
throws SQLException;
public abstract Number maxId(ValueType type);
public abstract Number minId(ValueType type);
public int code(Literal value) {
return shift(minId(valueOf(value)));
}
public long hashOf(Value value) {
final long span = 1152921504606846975l;
MessageDigest digest = md5.get();
long type = hashLiteralType(digest, value);
long hash = type * 31 + hash(digest, value.stringValue());
return hash & span | valueOf(value).index() * (span + 1);
}
public abstract Number nextId(Value value);
public boolean isLiteral(Number id) {
return valueOf(id).isLiteral();
}
public boolean isLong(Number id) {
return valueOf(id).isLong();
}
public boolean isURI(Number id) {
return valueOf(id).isURI();
}
public Number idOf(Value value) {
return idOf(hashOf(value));
}
public abstract Number idOf(Number number);
public ValueType valueOf(Number id) {
int idx = shift(id);
ValueType[] values = ValueType.values();
if (idx < 0 || idx >= values.length) {
throw new IllegalArgumentException("Invalid ID " + id);
}
return values[idx];
}
protected abstract int shift(Number id);
protected long hash(MessageDigest digest, String str) {
try {
digest.update(str.getBytes(UTF_8));
return new BigInteger(1, digest.digest()).longValue();
}
catch (UnsupportedEncodingException e) {
throw new AssertionError(e);
}
}
protected long hashLiteralType(MessageDigest digest, Value value) {
if (value instanceof Literal) {
Literal lit = (Literal)value;
if (lit.getDatatype() != null) {
return hash(digest, lit.getDatatype().stringValue());
}
if (lit.getLanguage() != null) {
return hash(digest, lit.getLanguage());
}
}
return 0;
}
private boolean isZoned(Literal lit) {
String stringValue = lit.stringValue();
int length = stringValue.length();
if (length < 1) {
return false;
}
if (stringValue.charAt(length - 1) == 'Z') {
return true;
}
if (length < 6) {
return false;
}
if (stringValue.charAt(length - 3) != ':') {
return false;
}
char chr = stringValue.charAt(length - 6);
return chr == '+' || chr == '-';
}
private ValueType valueOf(BNode value) {
return ValueType.BNODE;
}
protected ValueType valueOf(Literal lit) {
String lang = lit.getLanguage();
URI dt = lit.getDatatype();
int length = lit.stringValue().length();
if (lang != null) {
// language
if (length > LONG) {
return ValueType.LANG_LONG;
}
return ValueType.LANG;
}
if (dt == null) {
// simple
if (length > LONG) {
return ValueType.SIMPLE_LONG;
}
return ValueType.SIMPLE;
}
if (isNumericDatatype(dt)) {
return ValueType.NUMERIC;
}
if (isCalendarDatatype(dt)) {
// calendar
if (isZoned(lit)) {
return ValueType.DATETIME_ZONED;
}
return ValueType.DATETIME;
}
if (RDF.XMLLITERAL.equals(dt)) {
return ValueType.XML;
}
if (length > LONG) {
return ValueType.TYPED_LONG;
}
return ValueType.TYPED;
}
private ValueType valueOf(URI value) {
if (value.stringValue().length() > LONG) {
return ValueType.URI_LONG;
}
return ValueType.URI;
}
protected ValueType valueOf(Value value) {
if (value instanceof URI) {
return valueOf((URI)value);
}
if (value instanceof Literal) {
return valueOf((Literal)value);
}
assert value instanceof BNode : value;
return valueOf((BNode)value);
}
}