blob: 1ec3fc00255985b73f45a8a9af54060981d243b6 [file] [log] [blame]
/**
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.apache.marmotta.ldpath.backend.jena;
import com.google.common.base.Function;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterators;
import com.hp.hpl.jena.datatypes.TypeMapper;
import com.hp.hpl.jena.rdf.model.Literal;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.rdf.model.Resource;
import com.hp.hpl.jena.rdf.model.Statement;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Collection;
import java.util.Date;
import java.util.Locale;
import java.util.concurrent.ThreadPoolExecutor;
import org.apache.marmotta.ldpath.api.backend.RDFBackend;
import org.apache.marmotta.ldpath.util.FormatUtils;
/**
* Add file description here!
* <p/>
* Author: Sebastian Schaffert
*/
public class GenericJenaBackend implements RDFBackend<RDFNode> {
private Model model;
public GenericJenaBackend(Model model) {
this.model = model;
}
/**
* Return true if the underlying backend supports the parallel execution of queries.
*
* @return
*/
@Override
public boolean supportsThreading() {
return false;
}
/**
* In case the backend supports threading, this method should return the ExecutorService representing the
* thread pool. LDPath lets the backend manage the thread pool to avoid excessive threading.
*
* @return
*/
@Override
public ThreadPoolExecutor getThreadPool() {
return null;
}
/**
* Test whether the node passed as argument is a literal
*
* @param n the node to check
* @return true if the node is a literal
*/
@Override
public boolean isLiteral(RDFNode n) {
return n.isLiteral();
}
/**
* Test whether the node passed as argument is a URI
*
* @param n the node to check
* @return true if the node is a URI
*/
@Override
public boolean isURI(RDFNode n) {
return n.isURIResource();
}
/**
* Test whether the node passed as argument is a blank node
*
* @param n the node to check
* @return true if the node is a blank node
*/
@Override
public boolean isBlank(RDFNode n) {
return n.isAnon();
}
/**
* Return the language of the literal node passed as argument.
*
* @param n the literal node for which to return the language
* @return a Locale representing the language of the literal, or null if the literal node has no language
* @throws IllegalArgumentException in case the node is no literal
*/
@Override
public Locale getLiteralLanguage(RDFNode n) {
if(n.isLiteral()) {
if (((Literal)n).getLanguage() != null) {
return new Locale(((Literal)n).getLanguage());
} else {
return null;
}
} else {
throw new IllegalArgumentException("the node "+n+" is not a literal, cannot return language");
}
}
/**
* Return the URI of the type of the literal node passed as argument.
*
* @param n the literal node for which to return the typer
* @return a URI representing the type of the literal content, or null if the literal is untyped
* @throws IllegalArgumentException in case the node is no literal
*/
@Override
public URI getLiteralType(RDFNode n) {
if(n.isLiteral()) {
if (((Literal)n).getLanguage() != null) {
try {
return new URI(((Literal)n).getDatatypeURI());
} catch (URISyntaxException e) {
throw new IllegalArgumentException("the type of node "+n+" was not a valid URI");
}
} else {
return null;
}
} else {
throw new IllegalArgumentException("the node "+n+" is not a literal, cannot return literal type");
}
}
/**
* Create a literal node with the content passed as argument
*
* @param content string content to represent inside the literal
* @return a literal node in using the model used by this backend
*/
@Override
public RDFNode createLiteral(String content) {
return model.createLiteral(content);
}
/**
* Create a literal node with the content passed as argument
*
* @param content string content to represent inside the literal
* @return a literal node in using the model used by this backend
*/
@Override
public RDFNode createLiteral(String content, Locale language, URI type) {
if(language != null && type == null) {
return model.createLiteral(content,language.getLanguage());
} else if(language == null && type != null) {
return model.createTypedLiteral(content, TypeMapper.getInstance().getSafeTypeByName(type.toString()));
} else {
return model.createLiteral(content);
}
}
/**
* Create a URI mode with the URI passed as argument
*
* @param uri URI of the resource to create
* @return a URI node using the model used by this backend
*/
@Override
public RDFNode createURI(String uri) {
return model.createProperty(uri);
}
/**
* Return the lexial representation of a node. For a literal, this will be the content, for a URI node it will be the
* URI itself, and for a blank node it will be the identifier of the node.
*
* @param rdfNode
* @return
*/
@Override
public String stringValue(RDFNode rdfNode) {
if(isLiteral(rdfNode)) {
return ((Literal)rdfNode).getString();
} else if(isURI(rdfNode)) {
return ((Resource)rdfNode).getURI();
} else if(isBlank(rdfNode)) {
return ((Resource)rdfNode).getId().getLabelString();
} else {
return rdfNode.toString();
}
}
/**
* Return the double value of a literal node. Depending on the backend implementing this method,
* the value can be retrieved directly or must be parsed from the string representation. The method can throw
* a NumberFormatException or ArithmeticException indicating that the value cannot be represented as double, and an
* IllegalArgumentException, indicating that the passed node is not a literal
*
* @param rdfNode the literal node for which to return the double value
* @return double value of the literal node
* @throws NumberFormatException in case the literal cannot be represented as double value
* @throws ArithmeticException in case the literal cannot be represented as double value
* @throws IllegalArgumentException in case the node passed as argument is not a literal
*/
@Override
public Double doubleValue(RDFNode rdfNode) {
if(isLiteral(rdfNode)) {
return ((Literal)rdfNode).getDouble();
} else {
throw new IllegalArgumentException("the node "+rdfNode+" is not a literal value");
}
}
/**
* Return the long value of a literal node. Depending on the backend implementing this method,
* the value can be retrieved directly or must be parsed from the string representation. The method can throw
* a NumberFormatException or ArithmeticException indicating that the value cannot be represented as long, and an
* IllegalArgumentException, indicating that the passed node is not a literal
*
* @param rdfNode the literal node for which to return the long value
* @return long value of the literal node
* @throws NumberFormatException in case the literal cannot be represented as long value
* @throws ArithmeticException in case the literal cannot be represented as long value
* @throws IllegalArgumentException in case the node passed as argument is not a literal
*/
@Override
public Long longValue(RDFNode rdfNode) {
if(isLiteral(rdfNode)) {
return ((Literal)rdfNode).getLong();
} else {
throw new IllegalArgumentException("the node "+rdfNode+" is not a literal value");
}
}
/**
* Return the boolean value of a literal node. Depending on the backend implementing this method,
* the value can be retrieved directly or must be parsed from the string representation.
* TODO: Define:<ul>
* <li> Do we also support '0' '1', 'yes', 'no'; whats about case insensitive
* such as TRUE, False
* <li> should we throw an RuntimeException of not an boolean value or return
* false as {@link Boolean#parseBoolean(String)}
* </ul>
*
* @param rdfNode the literal node for which to return the boolean value
* @return long value of the literal node
* @throws IllegalArgumentException in case the node passed as argument is not a literal
*/
@Override
public Boolean booleanValue(RDFNode rdfNode) {
if(isLiteral(rdfNode)) {
return ((Literal)rdfNode).getBoolean();
} else {
throw new IllegalArgumentException("the node "+rdfNode+" is not a literal value");
}
}
/**
* TODO
*
* @param rdfNode the literal node for which to return the dateTime value
* @return long value of the literal node
* @throws IllegalArgumentException in case the node passed as argument is not a literal
*/
@Override
public Date dateTimeValue(RDFNode rdfNode) {
if(isLiteral(rdfNode)) {
return FormatUtils.parseDate(((Literal)rdfNode).getString());
} else {
throw new IllegalArgumentException("the node "+rdfNode+" is not a literal value");
}
}
/**
* TODO
*
* @param rdfNode the literal node for which to return the date value
* @return long value of the literal node
* @throws IllegalArgumentException in case the node passed as argument is not a literal
*/
@Override
public Date dateValue(RDFNode rdfNode) {
if(isLiteral(rdfNode)) {
return FormatUtils.parseDate(((Literal)rdfNode).getString());
} else {
throw new IllegalArgumentException("the node "+rdfNode+" is not a literal value");
}
}
/**
* TODO
*
* @param rdfNode the literal node for which to return the time value
* @return long value of the literal node
* @throws IllegalArgumentException in case the node passed as argument is not a literal
*/
@Override
public Date timeValue(RDFNode rdfNode) {
if(isLiteral(rdfNode)) {
return FormatUtils.parseDate(((Literal)rdfNode).getString());
} else {
throw new IllegalArgumentException("the node "+rdfNode+" is not a literal value");
}
}
/**
* Return the float value of a literal node. Depending on the backend implementing this method,
* the value can be retrieved directly or must be parsed from the string representation. The method can throw
* a NumberFormatException or ArithmeticException indicating that the value cannot be represented as float, and an
* IllegalArgumentException, indicating that the passed node is not a literal
*
* @param rdfNode the literal node for which to return the float value
* @return long value of the literal node
* @throws NumberFormatException in case the literal cannot be represented as float value
* @throws ArithmeticException in case the literal cannot be represented as float value
* @throws IllegalArgumentException in case the node passed as argument is not a literal
*/
@Override
public Float floatValue(RDFNode rdfNode) {
if(isLiteral(rdfNode)) {
return ((Literal)rdfNode).getFloat();
} else {
throw new IllegalArgumentException("the node "+rdfNode+" is not a literal value");
}
}
/**
* Return the 32bit integer value of a literal node. Depending on the backend implementing this method,
* the value can be retrieved directly or must be parsed from the string representation. The method can throw
* a NumberFormatException or ArithmeticException indicating that the value cannot be represented as integer, and an
* IllegalArgumentException, indicating that the passed node is not a literal.
* <p/>
* Note that this is restricted to 32bit singed integer values as defined by
* xsd:int and {@link Integer}. For bigger nuber one might want to use
* xsd:integer represented by {@link java.math.BigInteger}.
*
* @param rdfNode the literal node for which to return the Integer (xsd:int) value
* @return long value of the literal node
* @throws NumberFormatException in case the literal cannot be represented as 32 bit integer value
* @throws ArithmeticException in case the literal cannot be represented as 32 bit integer value
* @throws IllegalArgumentException in case the node passed as argument is not a literal
*/
@Override
public Integer intValue(RDFNode rdfNode) {
if(isLiteral(rdfNode)) {
return ((Literal)rdfNode).getInt();
} else {
throw new IllegalArgumentException("the node "+rdfNode+" is not a literal value");
}
}
/**
* Return the arbitrary length integer value of a literal node. Depending on the backend implementing this method,
* the value can be retrieved directly or must be parsed from the string representation. The method can throw
* a NumberFormatException or ArithmeticException indicating that the value cannot be represented as integer, and an
* IllegalArgumentException, indicating that the passed node is not a literal.
*
* @param rdfNode the literal node for which to return the {@link java.math.BigInteger xsd:integer} value
* @return long value of the literal node
* @throws NumberFormatException in case the literal cannot be represented as integer value
* @throws ArithmeticException in case the literal cannot be represented as long value
* @throws IllegalArgumentException in case the node passed as argument is integer a literal
*/
@Override
public BigInteger integerValue(RDFNode rdfNode) {
if(isLiteral(rdfNode)) {
return new BigInteger(((Literal)rdfNode).getString());
} else {
throw new IllegalArgumentException("the node "+rdfNode+" is not a literal value");
}
}
/**
* Return the decimal number of a literal node. Depending on the backend implementing this method,
* the value can be retrieved directly or must be parsed from the string representation. The method can throw
* a NumberFormatException or ArithmeticException indicating that the value cannot be represented as decimal, and an
* IllegalArgumentException, indicating that the passed node is not a literal.
*
* @param rdfNode the literal node for which to return the xsd:decimal value
* @return long value of the literal node
* @throws NumberFormatException in case the literal cannot be represented as decimal value
* @throws ArithmeticException in case the literal cannot be represented as decimal value
* @throws IllegalArgumentException in case the node passed as argument is not a literal
*/
@Override
public BigDecimal decimalValue(RDFNode rdfNode) {
if(isLiteral(rdfNode)) {
return new BigDecimal(((Literal)rdfNode).getString());
} else {
throw new IllegalArgumentException("the node "+rdfNode+" is not a literal value");
}
}
/**
* List the objects of triples in the triple store underlying this backend that have the subject and
* property given as argument.
*
* @param subject the subject of the triples to look for
* @param property the property of the triples to look for, <code>null</code> is interpreted as wildcard
* @return all objects of triples with matching subject and property
*/
@Override
public Collection<RDFNode> listObjects(RDFNode subject, RDFNode property) {
try {
return ImmutableSet.copyOf(
Iterators.transform(
model.listStatements((Resource)subject,(Property)property,(RDFNode)null),
new Function<Statement, RDFNode>() {
@Override
public RDFNode apply(Statement input) {
return input.getObject();
}
})
);
} catch(ClassCastException ex) {
throw new IllegalArgumentException("subject or property where no valid resources in the Jena model",ex);
}
}
/**
* List the subjects of triples in the triple store underlying this backend that have the object and
* property given as argument.
*
* @param object the object of the triples to look for
* @param property the property of the triples to look for, <code>null</code> is interpreted as wildcard
* @return all subjects of triples with matching object and property
* @throws UnsupportedOperationException in case reverse selection is not supported (e.g. when querying Linked Data)
*/
@Override
public Collection<RDFNode> listSubjects(RDFNode property, RDFNode object) {
try {
return ImmutableSet.copyOf(
Iterators.transform(
model.listStatements((Resource)null,(Property)property,object),
new Function<Statement, RDFNode>() {
@Override
public RDFNode apply(Statement input) {
return input.getSubject();
}
})
);
} catch(ClassCastException ex) {
throw new IllegalArgumentException("property was no valid resource in the Jena model",ex);
}
}
}