001: /*
002: * Copyright Aduna (http://www.aduna-software.com/) (c) 2008.
003: *
004: * Licensed under the Aduna BSD-style license.
005: */
006: package org.openrdf.sail.rdbms.schema;
007:
008: import static org.openrdf.model.datatypes.XMLDatatypeUtil.isCalendarDatatype;
009: import static org.openrdf.model.datatypes.XMLDatatypeUtil.isNumericDatatype;
010:
011: import java.io.UnsupportedEncodingException;
012: import java.math.BigInteger;
013: import java.security.MessageDigest;
014: import java.security.NoSuchAlgorithmException;
015: import java.sql.SQLException;
016:
017: import org.openrdf.model.BNode;
018: import org.openrdf.model.Literal;
019: import org.openrdf.model.URI;
020: import org.openrdf.model.Value;
021: import org.openrdf.model.vocabulary.RDF;
022:
023: /**
024: *
025: * @author James Leigh
026: */
027: public abstract class IdSequence {
028:
029: private static final String UTF_8 = "UTF-8";
030:
031: private static ThreadLocal<MessageDigest> md5 = new ThreadLocal<MessageDigest>() {
032:
033: @Override
034: protected MessageDigest initialValue() {
035: try {
036: return MessageDigest.getInstance("MD5");
037: } catch (NoSuchAlgorithmException e) {
038: throw new AssertionError(e);
039: }
040: }
041:
042: };
043:
044: /** 255 */
045: private int LONG = 255;
046:
047: private int MOD = 16;
048:
049: private HashTable table;
050:
051: public int getMod() {
052: return MOD;
053: }
054:
055: public abstract int getShift();
056:
057: public abstract int getJdbcIdType();
058:
059: public abstract String getSqlType();
060:
061: public HashTable getHashTable() {
062: return table;
063: }
064:
065: public void setHashTable(HashTable table) {
066: this .table = table;
067: }
068:
069: public abstract void init() throws SQLException;
070:
071: public abstract Number maxId(ValueType type);
072:
073: public abstract Number minId(ValueType type);
074:
075: public int code(Literal value) {
076: return shift(minId(valueOf(value)));
077: }
078:
079: public long hashOf(Value value) {
080: final long span = 1152921504606846975l;
081: MessageDigest digest = md5.get();
082: long type = hashLiteralType(digest, value);
083: long hash = type * 31 + hash(digest, value.stringValue());
084: return hash & span | valueOf(value).index() * (span + 1);
085: }
086:
087: public abstract Number nextId(Value value);
088:
089: public boolean isLiteral(Number id) {
090: return valueOf(id).isLiteral();
091: }
092:
093: public boolean isLong(Number id) {
094: return valueOf(id).isLong();
095: }
096:
097: public boolean isURI(Number id) {
098: return valueOf(id).isURI();
099: }
100:
101: public Number idOf(Value value) {
102: return idOf(hashOf(value));
103: }
104:
105: public abstract Number idOf(Number number);
106:
107: public ValueType valueOf(Number id) {
108: int idx = shift(id);
109: ValueType[] values = ValueType.values();
110: if (idx < 0 || idx >= values.length)
111: throw new IllegalArgumentException("Invalid ID " + id);
112: return values[idx];
113: }
114:
115: protected abstract int shift(Number id);
116:
117: protected long hash(MessageDigest digest, String str) {
118: try {
119: digest.update(str.getBytes(UTF_8));
120: return new BigInteger(1, digest.digest()).longValue();
121: } catch (UnsupportedEncodingException e) {
122: throw new AssertionError(e);
123: }
124: }
125:
126: protected long hashLiteralType(MessageDigest digest, Value value) {
127: if (value instanceof Literal) {
128: Literal lit = (Literal) value;
129: if (lit.getDatatype() != null)
130: return hash(digest, lit.getDatatype().stringValue());
131: if (lit.getLanguage() != null)
132: return hash(digest, lit.getLanguage());
133: }
134: return 0;
135: }
136:
137: private boolean isZoned(Literal lit) {
138: String stringValue = lit.stringValue();
139: int length = stringValue.length();
140: if (length < 1)
141: return false;
142: if (stringValue.charAt(length - 1) == 'Z')
143: return true;
144: if (length < 6)
145: return false;
146: if (stringValue.charAt(length - 3) != ':')
147: return false;
148: char chr = stringValue.charAt(length - 6);
149: return chr == '+' || chr == '-';
150: }
151:
152: private ValueType valueOf(BNode value) {
153: return ValueType.BNODE;
154: }
155:
156: protected ValueType valueOf(Literal lit) {
157: String lang = lit.getLanguage();
158: URI dt = lit.getDatatype();
159: int length = lit.stringValue().length();
160: if (lang != null) {
161: // language
162: if (length > LONG)
163: return ValueType.LANG_LONG;
164: return ValueType.LANG;
165: }
166: if (dt == null) {
167: // simple
168: if (length > LONG)
169: return ValueType.SIMPLE_LONG;
170: return ValueType.SIMPLE;
171: }
172: if (isNumericDatatype(dt))
173: return ValueType.NUMERIC;
174: if (isCalendarDatatype(dt)) {
175: // calendar
176: if (isZoned(lit))
177: return ValueType.DATETIME_ZONED;
178: return ValueType.DATETIME;
179: }
180: if (RDF.XMLLITERAL.equals(dt))
181: return ValueType.XML;
182: if (length > LONG)
183: return ValueType.TYPED_LONG;
184: return ValueType.TYPED;
185: }
186:
187: private ValueType valueOf(URI value) {
188: if (value.stringValue().length() > LONG)
189: return ValueType.URI_LONG;
190: return ValueType.URI;
191: }
192:
193: protected ValueType valueOf(Value value) {
194: if (value instanceof URI)
195: return valueOf((URI) value);
196: if (value instanceof Literal)
197: return valueOf((Literal) value);
198: assert value instanceof BNode : value;
199: return valueOf((BNode) value);
200: }
201: }
|