AbstractMultiTypeExtension.java
package org.wikidata.query.rdf.blazegraph.inline.literal;
import static java.util.Collections.unmodifiableSet;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import org.openrdf.model.Literal;
import org.openrdf.model.URI;
import org.openrdf.model.Value;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.bigdata.rdf.internal.IDatatypeURIResolver;
import com.bigdata.rdf.internal.IExtension;
import com.bigdata.rdf.internal.IV;
import com.bigdata.rdf.internal.impl.literal.AbstractLiteralIV;
import com.bigdata.rdf.internal.impl.literal.LiteralExtensionIV;
import com.bigdata.rdf.model.BigdataLiteral;
import com.bigdata.rdf.model.BigdataURI;
import com.bigdata.rdf.model.BigdataValue;
import com.bigdata.rdf.model.BigdataValueFactory;
import com.bigdata.util.InnerCause;
import com.google.common.collect.ImmutableMap;
import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
/**
* Abstract base class for implementing IExtension for multiple dataTypes.
*
* @param <V> Blazegraph value to expand. These are usually treated a bit
* roughly by Blazegraph - lots of rawtypes
*/
@SuppressFBWarnings("FCCD_FIND_CLASS_CIRCULAR_DEPENDENCY")
public abstract class AbstractMultiTypeExtension<V extends BigdataValue> implements IExtension<V> {
private static final Logger LOG = LoggerFactory.getLogger(AbstractMultiTypeExtension.class);
/**
* IV to type map as resolved against resolver provided on construction.
*/
@SuppressWarnings("rawtypes")
private final Map<IV, BigdataURI> dataTypes;
/**
* Set of data type uris resolved on construction.
*/
private final Set<BigdataURI> dataTypesSet;
public AbstractMultiTypeExtension(IDatatypeURIResolver resolver, Set<URI> supportedDataTypes) {
@SuppressWarnings("rawtypes")
ImmutableMap.Builder<IV, BigdataURI> dataTypesBuilder = ImmutableMap.builder();
for (URI uri : supportedDataTypes) {
BigdataURI val = resolver.resolve(uri);
dataTypesBuilder.put(val.getIV(), val);
}
this.dataTypes = dataTypesBuilder.build();
dataTypesSet = unmodifiableSet(new HashSet<>(this.dataTypes.values()));
}
@Override
public Set<BigdataURI> getDatatypes() {
return dataTypesSet;
}
@Override
@SuppressWarnings({"rawtypes", "unchecked", "checkstyle:illegalcatch"})
public LiteralExtensionIV createIV(Value value) {
if (!(value instanceof Literal)) {
throw new IllegalArgumentException("Expected a literal but got " + value);
}
Literal literal = (Literal) value;
try {
BigdataURI dt = resolveDataType(literal);
return new LiteralExtensionIV(createDelegateIV(literal, dt), dt.getIV());
} catch (Exception e) {
/*
* Exception logging in blazegraph isn't great for this so we log
* here as well
*/
LOG.warn("Couldn't create IV", e);
throw e;
}
}
@Override
@SuppressWarnings({"rawtypes", "unchecked", "checkstyle:illegalcatch"})
public V asValue(LiteralExtensionIV iv, BigdataValueFactory vf) {
BigdataURI dt = resolveDataType(iv);
try {
return (V) safeAsValue(iv, vf, dt);
} catch (RuntimeException ex) {
/*
* Catching this runtime exception is a Blazegraph idiom that we're
* just continuing to use.
*/
if (InnerCause.isInnerCause(ex, InterruptedException.class)) {
throw ex;
}
throw new IllegalArgumentException("bad iv: " + iv, ex);
}
}
/**
* Create the delegate iv for the literal.
*/
@SuppressWarnings("rawtypes")
protected abstract AbstractLiteralIV createDelegateIV(Literal literal, BigdataURI dt);
/**
* Convert the iv into the value. Its ok to throw exceptions here and the
* caller will catch them an make them ok with Blazegraph, thus "safe".
*/
@SuppressWarnings("rawtypes")
protected abstract BigdataLiteral safeAsValue(LiteralExtensionIV iv, BigdataValueFactory vf, BigdataURI dt);
/**
* Convert the literal into a uri as resolved by the resolve this extension
* received on construction.
*/
@SuppressWarnings("rawtypes")
protected BigdataURI resolveDataType(LiteralExtensionIV literal) {
IV extensionIV = literal.getExtensionIV();
BigdataURI dt = dataTypes.get(extensionIV);
if (dt == null) {
throw new IllegalArgumentException("Unrecognized datatype: " + extensionIV);
}
return dt;
}
/**
* Resolve the data type uri from the literal's type.
*/
protected BigdataURI resolveDataType(Literal literal) {
URI dt = literal.getDatatype();
if (dt == null) {
throw new IllegalArgumentException("Literal doesn't have a data type: " + literal);
}
// TODO why loop instead of use a hash set or something?
for (BigdataURI val : dataTypes.values()) {
// Note: URI.stringValue() is efficient....
if (val.stringValue().equals(dt.stringValue())) {
return val;
}
}
throw new IllegalArgumentException("Unrecognized data type: " + dt);
}
}