blob: 4c257537a3612af3a80f6d6c242c40316522a107 [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.linkeddata;
import org.apache.marmotta.ldcache.api.LDCachingBackend;
import org.apache.marmotta.ldcache.backend.infinispan.LDCachingInfinispanBackend;
import org.apache.marmotta.ldcache.model.CacheConfiguration;
import org.apache.marmotta.ldcache.services.LDCache;
import org.apache.marmotta.ldpath.api.backend.RDFBackend;
import org.openrdf.model.BNode;
import org.openrdf.model.Literal;
import org.openrdf.model.Value;
import org.openrdf.model.impl.LiteralImpl;
import org.openrdf.model.impl.URIImpl;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.xml.datatype.XMLGregorianCalendar;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;
import java.util.concurrent.ThreadPoolExecutor;
/**
* A Linked Data backend with persistent caching of the retrieved data. All data is read and stored in the directory
* passed as constructor argument.
* <p/>
* Author: Sebastian Schaffert
*/
public class LDCacheBackend implements RDFBackend<Value> {
private static final Logger log = LoggerFactory.getLogger(LDCacheBackend.class);
private LDCache ldcache;
public LDCacheBackend() {
LDCachingBackend backend = new LDCachingInfinispanBackend();
backend.initialize();
this.ldcache = new LDCache(new CacheConfiguration(), backend);
}
public LDCacheBackend(LDCache ldcache) {
this.ldcache = ldcache;
}
/**
* 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(Value n) {
return n instanceof Literal;
}
/**
* 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(Value n) {
return n instanceof org.openrdf.model.URI;
}
/**
* 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(Value n) {
return n instanceof BNode;
}
/**
* 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(Value n) {
try {
if(((Literal)n).getLanguage() != null) {
return new Locale( ((Literal)n).getLanguage() );
} else {
return null;
}
} catch (ClassCastException e) {
throw new IllegalArgumentException("Value "+n.stringValue()+" is not a literal" +
"but of type "+debugType(n));
}
}
/**
* 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(Value n) {
try {
if(((Literal)n).getDatatype() != null) {
try {
return new URI(((Literal)n).getDatatype().stringValue());
} catch (URISyntaxException e) {
log.error("literal datatype was not a valid URI: {}", ((Literal) n).getDatatype());
return null;
}
} else {
return null;
}
} catch (ClassCastException e) {
throw new IllegalArgumentException("Value "+n.stringValue()+" is not a literal" +
"but of type "+debugType(n));
}
}
/**
* Return the string value 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 value
* @return
*/
@Override
public String stringValue(Value value) {
return value.stringValue();
}
@Override
public BigDecimal decimalValue(Value node) {
try {
return ((Literal)node).decimalValue();
} catch (ClassCastException e) {
throw new IllegalArgumentException("Value "+node.stringValue()+" is not a literal" +
"but of type "+debugType(node));
}
}
@Override
public BigInteger integerValue(Value node) {
try {
return ((Literal)node).integerValue();
} catch (ClassCastException e) {
throw new IllegalArgumentException("Value "+node.stringValue()+" is not a literal" +
"but of type "+debugType(node));
}
}
@Override
public Boolean booleanValue(Value node) {
try {
return ((Literal)node).booleanValue();
} catch (ClassCastException e) {
throw new IllegalArgumentException("Value "+node.stringValue()+" is not a literal" +
"but of type "+debugType(node));
}
}
@Override
public Date dateTimeValue(Value node) {
try {
XMLGregorianCalendar cal = ((Literal)node).calendarValue();
//TODO: check if we need to deal with timezone and Local here
return cal.toGregorianCalendar().getTime();
} catch (ClassCastException e) {
throw new IllegalArgumentException("Value "+node.stringValue()+" is not a literal" +
"but of type "+debugType(node));
}
}
@Override
public Date dateValue(Value node) {
try {
XMLGregorianCalendar cal = ((Literal)node).calendarValue();
return new GregorianCalendar(cal.getYear(), cal.getMonth(), cal.getDay()).getTime();
} catch (ClassCastException e) {
throw new IllegalArgumentException("Value "+node.stringValue()+" is not a literal" +
"but of type "+debugType(node));
}
}
@Override
public Date timeValue(Value node) {
//TODO: Unless someone knows how to create a Date that only has the time
// from a XMLGregorianCalendar
return dateTimeValue(node);
}
@Override
public Long longValue(Value node) {
try {
return ((Literal)node).longValue();
} catch (ClassCastException e) {
throw new IllegalArgumentException("Value "+node.stringValue()+" is not a literal" +
"but of type "+debugType(node));
}
}
@Override
public Double doubleValue(Value node) {
try {
return ((Literal)node).doubleValue();
} catch (ClassCastException e) {
throw new IllegalArgumentException("Value "+node.stringValue()+" is not a literal" +
"but of type "+debugType(node));
}
}
@Override
public Float floatValue(Value node) {
try {
return ((Literal)node).floatValue();
} catch (ClassCastException e) {
throw new IllegalArgumentException("Value "+node.stringValue()+" is not a literal" +
"but of type "+debugType(node));
}
}
@Override
public Integer intValue(Value node) {
try {
return ((Literal)node).intValue();
} catch (ClassCastException e) {
throw new IllegalArgumentException("Value "+node.stringValue()+" is not a literal" +
"but of type "+debugType(node));
}
}
/**
* Prints the type (URI,bNode,literal) by inspecting the parsed {@link Value}
* to improve error messages and other loggings. In case of literals
* also the {@link #getLiteralType(Value) literal type} is printed
* @param value the value or <code>null</code>
* @return the type as string.
*/
protected String debugType(Value value) {
return value == null ? "null":isURI(value)?"URI":isBlank(value)?"bNode":
"literal ("+getLiteralType(value)+")";
}
@Override
public Literal createLiteral(String content) {
return new LiteralImpl(content);
}
@Override
public Literal createLiteral(String content, Locale language, URI type) {
log.debug("creating literal with content \"{}\", language {}, datatype {}",content,language,type);
if(language == null && type == null) {
return createLiteral(content);
} else if(type == null) {
return new LiteralImpl(content,language.getLanguage());
} else {
return new LiteralImpl(content, createURI(type.toString()));
}
}
@Override
public org.openrdf.model.URI createURI(String uri) {
return new URIImpl(uri);
}
/**
* @deprecated subject to be removed in the next release
*/
@Override
public boolean supportsThreading() {
return false;
}
/**
* @deprecated subject to be removed in the next release
*/
@Override
public ThreadPoolExecutor getThreadPool() {
return null;
}
/**
* 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<Value> listObjects(Value subject, Value property) {
log.info("retrieving resource {}", subject);
if (subject instanceof org.openrdf.model.URI) {
org.openrdf.model.URI s = (org.openrdf.model.URI) subject;
org.openrdf.model.URI p = (org.openrdf.model.URI) property;
return ldcache.get(s).filter(s, p, null).objects();
} else {
return Collections.emptyList();
}
}
/**
* List the subjects of triples in the triple store underlying this backend that have the object and
* property given as argument.
*
* @param property the property of the triples to look for, <code>null</code> is interpreted as wildcard
* @param object the object of the triples to look for
* @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<Value> listSubjects(Value property, Value object) {
throw new UnsupportedOperationException("reverse traversal not supported for Linked Data backend");
}
}