diff --git a/config-example.json b/config-example.json
index 4a5a721..11fcdf1 100644
--- a/config-example.json
+++ b/config-example.json
@@ -1,6 +1,11 @@
{
"title": "My Linked Data Fragments server",
+ "datasourcetypes": {
+ "HdtDatasource" : "org.linkeddatafragments.datasource.hdt.HdtDataSourceType",
+ "JenaTDBDatasource" : "org.linkeddatafragments.datasource.tdb.JenaTDBDataSourceType"
+ },
+
"datasources": {
"dbpedia": {
"title": "DBPedia",
diff --git a/src/org/linkeddatafragments/config/ConfigReader.java b/src/org/linkeddatafragments/config/ConfigReader.java
index a3240cc..7b2d99a 100644
--- a/src/org/linkeddatafragments/config/ConfigReader.java
+++ b/src/org/linkeddatafragments/config/ConfigReader.java
@@ -5,6 +5,8 @@
import java.util.Map;
import java.util.Map.Entry;
+import org.linkeddatafragments.datasource.IDataSourceType;
+
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonParser;
@@ -13,8 +15,10 @@
* Reads the configuration of a Linked Data Fragments server.
*
* @author Ruben Verborgh
+ * @author Olaf Hartig
*/
public class ConfigReader {
+ private final Map dataSourceTypes = new HashMap<>();
private final Map dataSources = new HashMap<>();
private final Map prefixes = new HashMap<>();
private final String baseURL;
@@ -28,6 +32,10 @@ public ConfigReader(Reader configReader) {
JsonObject root = new JsonParser().parse(configReader).getAsJsonObject();
this.baseURL = root.has("baseURL") ? root.getAsJsonPrimitive("baseURL").getAsString() : null;
+ for (Entry entry : root.getAsJsonObject("datasourcetypes").entrySet()) {
+ final String className = entry.getValue().getAsString();
+ dataSourceTypes.put(entry.getKey(), initDataSouceType(className) );
+ }
for (Entry entry : root.getAsJsonObject("datasources").entrySet()) {
JsonObject dataSource = entry.getValue().getAsJsonObject();
this.dataSources.put(entry.getKey(), dataSource);
@@ -37,6 +45,15 @@ public ConfigReader(Reader configReader) {
}
}
+ /**
+ * Gets the data source types.
+ *
+ * @return a mapping of names of data source types to these types
+ */
+ public Map getDataSourceTypes() {
+ return dataSourceTypes;
+ }
+
/**
* Gets the data sources.
*
@@ -58,4 +75,35 @@ public Map getPrefixes() {
public String getBaseURL() {
return baseURL;
}
+
+ protected IDataSourceType initDataSouceType( final String className )
+ {
+ final Class> c;
+ try {
+ c = Class.forName( className );
+ }
+ catch ( ClassNotFoundException e ) {
+ throw new IllegalArgumentException( "Class not found: " + className,
+ e );
+ }
+
+ final Object o;
+ try {
+ o = c.newInstance();
+ }
+ catch ( Exception e ) {
+ throw new IllegalArgumentException(
+ "Creating an instance of class '" + className + "' " +
+ "caused a " + e.getClass().getSimpleName() + ": " +
+ e.getMessage(), e );
+ }
+
+ if ( ! (o instanceof IDataSourceType) )
+ throw new IllegalArgumentException(
+ "Class '" + className + "' is not an implementation " +
+ "of IDataSourceType." );
+
+ return (IDataSourceType) o;
+ }
+
}
diff --git a/src/org/linkeddatafragments/datasource/AbstractJenaBasedRequestProcessorForTriplePatterns.java b/src/org/linkeddatafragments/datasource/AbstractJenaBasedRequestProcessorForTriplePatterns.java
deleted file mode 100644
index 8fba309..0000000
--- a/src/org/linkeddatafragments/datasource/AbstractJenaBasedRequestProcessorForTriplePatterns.java
+++ /dev/null
@@ -1,154 +0,0 @@
-package org.linkeddatafragments.datasource;
-
-import java.util.regex.Matcher;
-import java.util.regex.Pattern;
-
-import org.linkeddatafragments.fragments.LinkedDataFragment;
-import org.linkeddatafragments.fragments.tpf.TriplePatternFragmentRequest;
-import org.linkeddatafragments.util.CommonResources;
-
-import com.hp.hpl.jena.datatypes.TypeMapper;
-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.ResourceFactory;
-
-/**
- * Base class for implementations of {@link IFragmentRequestProcessor} that
- * process {@link TriplePatternFragmentRequest}s based on the Jena API.
- *
- * @author Olaf Hartig
- * @author Ruben Verborgh
- */
-abstract public class AbstractJenaBasedRequestProcessorForTriplePatterns
- extends AbstractRequestProcessorForTriplePatterns
-{
- abstract static protected class Worker
- extends AbstractRequestProcessorForTriplePatterns.Worker
- {
- public Worker( final TriplePatternFragmentRequest request )
- {
- super( request );
- }
-
- @Override
- protected LinkedDataFragment createFragment( final String subject,
- final String predicate,
- final String object,
- final long offset,
- final long limit )
- throws IllegalArgumentException
- {
- final Resource s = parseAsResource( subject );
- final Property p = parseAsProperty( predicate );
- final RDFNode o = parseAsNode( object );
-
- return createFragment( s, p, o, offset, limit );
- }
-
- abstract protected LinkedDataFragment createFragment(
- final Resource subject,
- final Property predicate,
- final RDFNode object,
- final long offset,
- final long limit )
- throws IllegalArgumentException;
-
- /**
- * Parses the given value as an RDF resource.
- *
- * @param value the value
- * @return the parsed value, or null if unspecified
- */
- public Resource parseAsResource( String value )
- {
- RDFNode subject = parseAsNode( value );
- return subject == null || subject instanceof Resource
- ? (Resource) subject
- : CommonResources.INVALID_URI;
- }
-
- /**
- * Parses the given value as an RDF property.
- *
- * @param value the value
- * @return the parsed value, or null if unspecified
- */
- public Property parseAsProperty( String value )
- {
- RDFNode predicateNode = parseAsNode( value );
- if ( predicateNode instanceof Resource ) {
- final String uri = ( (Resource) predicateNode ).getURI();
- return ResourceFactory.createProperty( uri );
- }
- else if ( predicateNode == null ) {
- return null;
- }
- else {
- return CommonResources.INVALID_URI;
- }
- }
-
- public final static TypeMapper TYPES = TypeMapper.getInstance();
- public final static Pattern STRINGPATTERN
- = Pattern.compile("^\"(.*)\"(?:@(.*)|\\^\\^([^<>]*)>?)?$");
-
- /**
- * Parses the given value as an RDF node.
- *
- * @param value the value
- * @return the parsed value, or null if unspecified
- */
- public RDFNode parseAsNode( String value )
- {
- // nothing or empty indicates an unknown
- if ( value == null || value.isEmpty() ) {
- return null;
- }
-
- // find the kind of entity based on the first character
- char firstChar = value.charAt(0);
- switch ( firstChar )
- {
- // variable or blank node indicates an unknown
- case '?':
- case '_':
- return null;
-
- // angular brackets indicate a URI
- case '<':
- return ResourceFactory.createResource(
- value.substring(1, value.length() - 1) );
-
- // quotes indicate a string
- case '"':
- Matcher matcher = STRINGPATTERN.matcher( value );
- if ( matcher.matches() ) {
- String body = matcher.group(1);
- String lang = matcher.group(2);
- String type = matcher.group(3);
- if ( lang != null ) {
- return ResourceFactory.createLangLiteral(
- body, lang );
- }
- else if ( type != null ) {
- return ResourceFactory.createTypedLiteral(
- body, TYPES.getSafeTypeByName(type) );
- }
- else {
- return ResourceFactory.createPlainLiteral( body );
- }
- }
- else {
- return CommonResources.INVALID_URI;
- }
-
- // assume it's a URI without angular brackets
- default:
- return ResourceFactory.createResource( value );
- }
- }
-
- } // end of class Worker
-
-}
diff --git a/src/org/linkeddatafragments/datasource/AbstractRequestProcessorForTriplePatterns.java b/src/org/linkeddatafragments/datasource/AbstractRequestProcessorForTriplePatterns.java
index 36cff95..9ba984a 100644
--- a/src/org/linkeddatafragments/datasource/AbstractRequestProcessorForTriplePatterns.java
+++ b/src/org/linkeddatafragments/datasource/AbstractRequestProcessorForTriplePatterns.java
@@ -1,41 +1,52 @@
package org.linkeddatafragments.datasource;
+import com.hp.hpl.jena.rdf.model.Model;
+
import org.linkeddatafragments.fragments.LinkedDataFragment;
import org.linkeddatafragments.fragments.LinkedDataFragmentRequest;
+import org.linkeddatafragments.fragments.tpf.TriplePatternElement;
import org.linkeddatafragments.fragments.tpf.TriplePatternFragment;
import org.linkeddatafragments.fragments.tpf.TriplePatternFragmentImpl;
import org.linkeddatafragments.fragments.tpf.TriplePatternFragmentRequest;
-import com.hp.hpl.jena.rdf.model.Model;
-
/**
* Base class for implementations of {@link IFragmentRequestProcessor} that
* process {@link TriplePatternFragmentRequest}s.
*
+ * @param type for representing RDF terms in triple patterns
+ * @param type for representing specific variables in triple patterns
+ *
* @author Olaf Hartig
*/
-public abstract class AbstractRequestProcessorForTriplePatterns
- extends AbstractRequestProcessor
+public abstract class
+ AbstractRequestProcessorForTriplePatterns
+ extends AbstractRequestProcessor
{
@Override
- protected Worker getWorker( final LinkedDataFragmentRequest request )
+ protected final Worker getWorker(
+ final LinkedDataFragmentRequest request )
throws IllegalArgumentException
{
- if ( request instanceof TriplePatternFragmentRequest )
- return getWorker( (TriplePatternFragmentRequest) request );
+ if ( request instanceof TriplePatternFragmentRequest,?> ) {
+ @SuppressWarnings("unchecked")
+ final TriplePatternFragmentRequest tpfRequest =
+ (TriplePatternFragmentRequest) request;
+ return getTPFSpecificWorker( tpfRequest );
+ }
else
throw new IllegalArgumentException( request.getClass().getName() );
}
- abstract protected Worker getWorker(
- final TriplePatternFragmentRequest request )
+ abstract protected Worker getTPFSpecificWorker(
+ final TriplePatternFragmentRequest request )
throws IllegalArgumentException;
- abstract static protected class Worker
+ abstract static protected class Worker
extends AbstractRequestProcessor.Worker
{
- public Worker( final TriplePatternFragmentRequest request )
+ public Worker(
+ final TriplePatternFragmentRequest request )
{
super( request );
}
@@ -51,8 +62,9 @@ public LinkedDataFragment createRequestedFragment()
else
offset = 0L;
- final TriplePatternFragmentRequest tpfRequest =
- (TriplePatternFragmentRequest) request;
+ @SuppressWarnings("unchecked")
+ final TriplePatternFragmentRequest tpfRequest =
+ (TriplePatternFragmentRequest) request;
return createFragment( tpfRequest.getSubject(),
tpfRequest.getPredicate(),
@@ -60,11 +72,12 @@ public LinkedDataFragment createRequestedFragment()
offset, limit );
}
- abstract protected LinkedDataFragment createFragment( final String subj,
- final String pred,
- final String obj,
- final long offset,
- final long limit )
+ abstract protected LinkedDataFragment createFragment(
+ final TriplePatternElement subj,
+ final TriplePatternElement pred,
+ final TriplePatternElement obj,
+ final long offset,
+ final long limit )
throws IllegalArgumentException;
protected TriplePatternFragment createEmptyTriplePatternFragment()
@@ -78,17 +91,11 @@ protected TriplePatternFragment createTriplePatternFragment(
final long totalSize,
final boolean isLastPage )
{
- final long pageNumber;
- if ( request.isPageRequest() )
- pageNumber = request.getPageNumber();
- else
- pageNumber = 1L;
-
return new TriplePatternFragmentImpl( triples,
totalSize,
request.getFragmentURL(),
request.getDatasetURL(),
- pageNumber,
+ request.getPageNumber(),
isLastPage );
}
diff --git a/src/org/linkeddatafragments/datasource/DataSource.java b/src/org/linkeddatafragments/datasource/DataSource.java
index 63344bd..465c9a0 100644
--- a/src/org/linkeddatafragments/datasource/DataSource.java
+++ b/src/org/linkeddatafragments/datasource/DataSource.java
@@ -1,10 +1,5 @@
package org.linkeddatafragments.datasource;
-import org.linkeddatafragments.fragments.IFragmentRequestParser;
-import org.linkeddatafragments.fragments.LinkedDataFragmentRequest;
-import org.linkeddatafragments.fragments.tpf.TPFRequestParser;
-import org.linkeddatafragments.fragments.tpf.TriplePatternFragmentRequest;
-
/**
*
* @author mielvandersande
@@ -29,19 +24,6 @@ public String getTitle() {
return this.title;
};
- /**
- * This implementation assumes that requests are
- * {@link TriplePatternFragmentRequest}s.
- *
- * Data sources for other types of {@link LinkedDataFragmentRequest}s must
- * override this method accordingly.
- */
- @Override
- public IFragmentRequestParser getRequestParser()
- {
- return new TPFRequestParser();
- }
-
@Override
public void close() {}
}
diff --git a/src/org/linkeddatafragments/datasource/hdt/HdtBasedRequestProcessorForTPFs.java b/src/org/linkeddatafragments/datasource/hdt/HdtBasedRequestProcessorForTPFs.java
index 7eff94f..3ed8dbe 100644
--- a/src/org/linkeddatafragments/datasource/hdt/HdtBasedRequestProcessorForTPFs.java
+++ b/src/org/linkeddatafragments/datasource/hdt/HdtBasedRequestProcessorForTPFs.java
@@ -2,9 +2,10 @@
import java.io.IOException;
-import org.linkeddatafragments.datasource.AbstractJenaBasedRequestProcessorForTriplePatterns;
+import org.linkeddatafragments.datasource.AbstractRequestProcessorForTriplePatterns;
import org.linkeddatafragments.datasource.IFragmentRequestProcessor;
import org.linkeddatafragments.fragments.LinkedDataFragment;
+import org.linkeddatafragments.fragments.tpf.TriplePatternElement;
import org.linkeddatafragments.fragments.tpf.TriplePatternFragmentRequest;
import org.rdfhdt.hdt.enums.TripleComponentRole;
import org.rdfhdt.hdt.hdt.HDT;
@@ -16,9 +17,7 @@
import com.hp.hpl.jena.graph.Triple;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
-import com.hp.hpl.jena.rdf.model.Property;
import com.hp.hpl.jena.rdf.model.RDFNode;
-import com.hp.hpl.jena.rdf.model.Resource;
/**
* Implementation of {@link IFragmentRequestProcessor} that processes
@@ -28,7 +27,7 @@
* @author Olaf Hartig
*/
public class HdtBasedRequestProcessorForTPFs
- extends AbstractJenaBasedRequestProcessorForTriplePatterns
+ extends AbstractRequestProcessorForTriplePatterns
{
protected final HDT datasource;
protected final NodeDictionary dictionary;
@@ -46,7 +45,8 @@ public HdtBasedRequestProcessorForTPFs( String hdtFile ) throws IOException
}
@Override
- protected Worker getWorker( final TriplePatternFragmentRequest request )
+ protected Worker getTPFSpecificWorker(
+ final TriplePatternFragmentRequest request )
throws IllegalArgumentException
{
return new Worker( request );
@@ -54,23 +54,30 @@ protected Worker getWorker( final TriplePatternFragmentRequest request )
protected class Worker
- extends AbstractJenaBasedRequestProcessorForTriplePatterns.Worker
+ extends AbstractRequestProcessorForTriplePatterns.Worker
{
- public Worker( final TriplePatternFragmentRequest request ) {
- super( request );
+ public Worker( final TriplePatternFragmentRequest req )
+ {
+ super( req );
}
@Override
- protected LinkedDataFragment createFragment( final Resource subject,
- final Property predicate,
- final RDFNode object,
- final long offset,
- final long limit )
+ protected LinkedDataFragment createFragment(
+ final TriplePatternElement subject,
+ final TriplePatternElement predicate,
+ final TriplePatternElement object,
+ final long offset,
+ final long limit )
{
+ // FIXME: The following algorithm is incorrect for cases in which
+ // the requested triple pattern contains a specific variable
+ // multiple times (e.g., ?x foaf:knows ?x ).
+ // see https://github.com/LinkedDataFragments/Server.Java/issues/23
+
// look up the result from the HDT datasource)
- int subjectId = subject == null ? 0 : dictionary.getIntID(subject.asNode(), TripleComponentRole.SUBJECT);
- int predicateId = predicate == null ? 0 : dictionary.getIntID(predicate.asNode(), TripleComponentRole.PREDICATE);
- int objectId = object == null ? 0 : dictionary.getIntID(object.asNode(), TripleComponentRole.OBJECT);
+ int subjectId = subject.isVariable() ? 0 : dictionary.getIntID(subject.asTerm().asNode(), TripleComponentRole.SUBJECT);
+ int predicateId = predicate.isVariable() ? 0 : dictionary.getIntID(predicate.asTerm().asNode(), TripleComponentRole.PREDICATE);
+ int objectId = object.isVariable() ? 0 : dictionary.getIntID(object.asTerm().asNode(), TripleComponentRole.OBJECT);
if (subjectId < 0 || predicateId < 0 || objectId < 0) {
return createEmptyTriplePatternFragment();
diff --git a/src/org/linkeddatafragments/datasource/hdt/HdtDataSource.java b/src/org/linkeddatafragments/datasource/hdt/HdtDataSource.java
index 2b32c78..03d7e19 100644
--- a/src/org/linkeddatafragments/datasource/hdt/HdtDataSource.java
+++ b/src/org/linkeddatafragments/datasource/hdt/HdtDataSource.java
@@ -4,6 +4,8 @@
import org.linkeddatafragments.datasource.DataSource;
import org.linkeddatafragments.datasource.IFragmentRequestProcessor;
+import org.linkeddatafragments.fragments.IFragmentRequestParser;
+import org.linkeddatafragments.fragments.tpf.TPFRequestParserForJenaBackends;
/**
* An HDT data source of Basic Linked Data Fragments.
@@ -28,6 +30,12 @@ public HdtDataSource(String title, String description, String hdtFile) throws IO
requestProcessor = new HdtBasedRequestProcessorForTPFs( hdtFile );
}
+ @Override
+ public IFragmentRequestParser getRequestParser()
+ {
+ return TPFRequestParserForJenaBackends.getInstance();
+ }
+
@Override
public IFragmentRequestProcessor getRequestProcessor()
{
diff --git a/src/org/linkeddatafragments/datasource/hdt/HdtDataSourceType.java b/src/org/linkeddatafragments/datasource/hdt/HdtDataSourceType.java
index d72e2aa..2826c2f 100644
--- a/src/org/linkeddatafragments/datasource/hdt/HdtDataSourceType.java
+++ b/src/org/linkeddatafragments/datasource/hdt/HdtDataSourceType.java
@@ -3,7 +3,6 @@
import java.io.File;
import java.io.IOException;
-import org.linkeddatafragments.datasource.DataSourceTypesRegistry;
import org.linkeddatafragments.datasource.IDataSource;
import org.linkeddatafragments.datasource.IDataSourceType;
import org.linkeddatafragments.exceptions.DataSourceException;
@@ -17,15 +16,6 @@
*/
public class HdtDataSourceType implements IDataSourceType
{
- public static final String TYPE_NAME = "HdtDatasource";
-
- public static void register() {
- if ( ! DataSourceTypesRegistry.isRegistered(TYPE_NAME) ) {
- DataSourceTypesRegistry.register( TYPE_NAME,
- new HdtDataSourceType() );
- }
- }
-
@Override
public IDataSource createDataSource( final String title,
final String description,
diff --git a/src/org/linkeddatafragments/datasource/index/IndexDataSource.java b/src/org/linkeddatafragments/datasource/index/IndexDataSource.java
index 70ffb0f..72f6267 100644
--- a/src/org/linkeddatafragments/datasource/index/IndexDataSource.java
+++ b/src/org/linkeddatafragments/datasource/index/IndexDataSource.java
@@ -5,6 +5,8 @@
import org.linkeddatafragments.datasource.DataSource;
import org.linkeddatafragments.datasource.IDataSource;
import org.linkeddatafragments.datasource.IFragmentRequestProcessor;
+import org.linkeddatafragments.fragments.IFragmentRequestParser;
+import org.linkeddatafragments.fragments.tpf.TPFRequestParserForJenaBackends;
/**
* An Index data source provides an overview of all available datasets.
@@ -21,6 +23,12 @@ public IndexDataSource(String baseUrl, HashMap datasources)
requestProcessor = new IndexRequestProcessorForTPFs( baseUrl, datasources );
}
+ @Override
+ public IFragmentRequestParser getRequestParser()
+ {
+ return TPFRequestParserForJenaBackends.getInstance();
+ }
+
@Override
public IFragmentRequestProcessor getRequestProcessor()
{
diff --git a/src/org/linkeddatafragments/datasource/index/IndexRequestProcessorForTPFs.java b/src/org/linkeddatafragments/datasource/index/IndexRequestProcessorForTPFs.java
index b9087e0..8e4c6d4 100644
--- a/src/org/linkeddatafragments/datasource/index/IndexRequestProcessorForTPFs.java
+++ b/src/org/linkeddatafragments/datasource/index/IndexRequestProcessorForTPFs.java
@@ -5,16 +5,18 @@
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.ResourceFactory;
import com.hp.hpl.jena.rdf.model.StmtIterator;
import com.hp.hpl.jena.rdf.model.impl.PropertyImpl;
import com.hp.hpl.jena.rdf.model.impl.ResourceImpl;
import java.util.HashMap;
import java.util.Map;
-import org.linkeddatafragments.datasource.AbstractJenaBasedRequestProcessorForTriplePatterns;
+import org.linkeddatafragments.datasource.AbstractRequestProcessorForTriplePatterns;
import org.linkeddatafragments.datasource.IDataSource;
import org.linkeddatafragments.datasource.IFragmentRequestProcessor;
import org.linkeddatafragments.fragments.LinkedDataFragment;
+import org.linkeddatafragments.fragments.tpf.TriplePatternElement;
import org.linkeddatafragments.fragments.tpf.TriplePatternFragmentRequest;
/**
@@ -26,7 +28,7 @@
* @author Olaf Hartig
*/
public class IndexRequestProcessorForTPFs
- extends AbstractJenaBasedRequestProcessorForTriplePatterns
+ extends AbstractRequestProcessorForTriplePatterns
{
final static String RDF = "http://www.w3.org/1999/02/22-rdf-syntax-ns#";
final static String RDFS = "http://www.w3.org/2000/01/rdf-schema#";
@@ -55,7 +57,8 @@ public IndexRequestProcessorForTPFs(
}
@Override
- protected Worker getWorker( final TriplePatternFragmentRequest request )
+ protected Worker getTPFSpecificWorker(
+ final TriplePatternFragmentRequest request )
throws IllegalArgumentException
{
return new Worker( request );
@@ -63,19 +66,33 @@ protected Worker getWorker( final TriplePatternFragmentRequest request )
protected class Worker
- extends AbstractJenaBasedRequestProcessorForTriplePatterns.Worker
+ extends AbstractRequestProcessorForTriplePatterns.Worker
{
- public Worker( final TriplePatternFragmentRequest request ) {
- super( request );
+ public Worker( final TriplePatternFragmentRequest req )
+ {
+ super( req );
}
@Override
- protected LinkedDataFragment createFragment( final Resource subject,
- final Property predicate,
- final RDFNode object,
- final long offset,
- final long limit )
+ protected LinkedDataFragment createFragment(
+ final TriplePatternElement s,
+ final TriplePatternElement p,
+ final TriplePatternElement o,
+ final long offset,
+ final long limit )
{
+ // FIXME: The following algorithm is incorrect for cases in which
+ // the requested triple pattern contains a specific variable
+ // multiple times (e.g., ?x foaf:knows ?x ).
+ // see https://github.com/LinkedDataFragments/Server.Java/issues/25
+
+ final Resource subject = s.isVariable() ? null
+ : s.asTerm().asResource();
+ final Property predicate = p.isVariable() ? null
+ : ResourceFactory.createProperty(p.asTerm().asResource().getURI());
+ final RDFNode object = o.isVariable() ? null
+ : o.asTerm();
+
StmtIterator listStatements = model.listStatements(subject, predicate, object);
Model result = ModelFactory.createDefaultModel();
diff --git a/src/org/linkeddatafragments/datasource/tdb/JenaTDBBasedRequestProcessorForTPFs.java b/src/org/linkeddatafragments/datasource/tdb/JenaTDBBasedRequestProcessorForTPFs.java
index 0360ddc..094183e 100644
--- a/src/org/linkeddatafragments/datasource/tdb/JenaTDBBasedRequestProcessorForTPFs.java
+++ b/src/org/linkeddatafragments/datasource/tdb/JenaTDBBasedRequestProcessorForTPFs.java
@@ -12,15 +12,14 @@
import com.hp.hpl.jena.rdf.model.Literal;
import com.hp.hpl.jena.rdf.model.Model;
import com.hp.hpl.jena.rdf.model.ModelFactory;
-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.tdb.TDBFactory;
import java.io.File;
-import org.linkeddatafragments.datasource.AbstractJenaBasedRequestProcessorForTriplePatterns;
+import org.linkeddatafragments.datasource.AbstractRequestProcessorForTriplePatterns;
import org.linkeddatafragments.datasource.IFragmentRequestProcessor;
import org.linkeddatafragments.fragments.LinkedDataFragment;
+import org.linkeddatafragments.fragments.tpf.TriplePatternElement;
import org.linkeddatafragments.fragments.tpf.TriplePatternFragmentRequest;
/**
@@ -31,7 +30,7 @@
* @author Olaf Hartig
*/
public class JenaTDBBasedRequestProcessorForTPFs
- extends AbstractJenaBasedRequestProcessorForTriplePatterns
+ extends AbstractRequestProcessorForTriplePatterns
{
private final Dataset tdb;
private final String sparql = "CONSTRUCT WHERE { ?s ?p ?o } " +
@@ -43,7 +42,8 @@ public class JenaTDBBasedRequestProcessorForTPFs
private final Query countQuery = QueryFactory.create(count, Syntax.syntaxSPARQL_11);
@Override
- protected Worker getWorker( final TriplePatternFragmentRequest request )
+ protected Worker getTPFSpecificWorker(
+ final TriplePatternFragmentRequest request )
throws IllegalArgumentException
{
return new Worker( request );
@@ -51,29 +51,36 @@ protected Worker getWorker( final TriplePatternFragmentRequest request )
protected class Worker
- extends AbstractJenaBasedRequestProcessorForTriplePatterns.Worker
+ extends AbstractRequestProcessorForTriplePatterns.Worker
{
- public Worker( final TriplePatternFragmentRequest request ) {
- super( request );
+ public Worker( final TriplePatternFragmentRequest req )
+ {
+ super( req );
}
@Override
- protected LinkedDataFragment createFragment( final Resource subject,
- final Property predicate,
- final RDFNode object,
- final long offset,
- final long limit )
+ protected LinkedDataFragment createFragment(
+ final TriplePatternElement subject,
+ final TriplePatternElement predicate,
+ final TriplePatternElement object,
+ final long offset,
+ final long limit )
{
+ // FIXME: The following algorithm is incorrect for cases in which
+ // the requested triple pattern contains a specific variable
+ // multiple times (e.g., ?x foaf:knows ?x ).
+ // see https://github.com/LinkedDataFragments/Server.Java/issues/24
+
Model model = tdb.getDefaultModel();
QuerySolutionMap map = new QuerySolutionMap();
- if (subject != null) {
- map.add("s", subject);
+ if ( ! subject.isVariable() ) {
+ map.add("s", subject.asTerm());
}
- if (predicate != null) {
- map.add("p", predicate);
+ if ( ! predicate.isVariable() ) {
+ map.add("p", predicate.asTerm());
}
- if (object != null) {
- map.add("o", object);
+ if ( ! object.isVariable() ) {
+ map.add("o", object.asTerm());
}
query.setOffset(offset);
diff --git a/src/org/linkeddatafragments/datasource/tdb/JenaTDBDataSource.java b/src/org/linkeddatafragments/datasource/tdb/JenaTDBDataSource.java
index a08473a..698a644 100644
--- a/src/org/linkeddatafragments/datasource/tdb/JenaTDBDataSource.java
+++ b/src/org/linkeddatafragments/datasource/tdb/JenaTDBDataSource.java
@@ -4,6 +4,8 @@
import org.linkeddatafragments.datasource.DataSource;
import org.linkeddatafragments.datasource.IFragmentRequestProcessor;
+import org.linkeddatafragments.fragments.IFragmentRequestParser;
+import org.linkeddatafragments.fragments.tpf.TPFRequestParserForJenaBackends;
/**
* Experimental Jena TDB-backed data source of Basic Linked Data Fragments.
@@ -15,6 +17,12 @@ public class JenaTDBDataSource extends DataSource {
protected final JenaTDBBasedRequestProcessorForTPFs requestProcessor;
+ @Override
+ public IFragmentRequestParser getRequestParser()
+ {
+ return TPFRequestParserForJenaBackends.getInstance();
+ }
+
@Override
public IFragmentRequestProcessor getRequestProcessor()
{
diff --git a/src/org/linkeddatafragments/datasource/tdb/JenaTDBDataSourceType.java b/src/org/linkeddatafragments/datasource/tdb/JenaTDBDataSourceType.java
index 5f38eee..3be3dea 100644
--- a/src/org/linkeddatafragments/datasource/tdb/JenaTDBDataSourceType.java
+++ b/src/org/linkeddatafragments/datasource/tdb/JenaTDBDataSourceType.java
@@ -2,7 +2,6 @@
import java.io.File;
-import org.linkeddatafragments.datasource.DataSourceTypesRegistry;
import org.linkeddatafragments.datasource.IDataSource;
import org.linkeddatafragments.datasource.IDataSourceType;
import org.linkeddatafragments.exceptions.DataSourceException;
@@ -17,15 +16,6 @@
*/
public class JenaTDBDataSourceType implements IDataSourceType
{
- public static final String TYPE_NAME = "JenaTDBDatasource";
-
- public static void register() {
- if ( ! DataSourceTypesRegistry.isRegistered(TYPE_NAME) ) {
- DataSourceTypesRegistry.register( TYPE_NAME,
- new JenaTDBDataSourceType() );
- }
- }
-
@Override
public IDataSource createDataSource( final String title,
final String description,
diff --git a/src/org/linkeddatafragments/fragments/LinkedDataFragmentRequest.java b/src/org/linkeddatafragments/fragments/LinkedDataFragmentRequest.java
index 0cbe701..3cc12c0 100644
--- a/src/org/linkeddatafragments/fragments/LinkedDataFragmentRequest.java
+++ b/src/org/linkeddatafragments/fragments/LinkedDataFragmentRequest.java
@@ -28,11 +28,9 @@ public interface LinkedDataFragmentRequest
boolean isPageRequest();
/**
- * Returns the number of the page requested for the LDF, if any (thatis,
- * if {@link #isPageOnly()} returns true).
- *
- * @throws UnsupportedOperationException
- * If the request is not for a specific page.
+ * Returns the number of the page requested for the LDF; if this is not a
+ * page-based request (that is, if {@link #isPageRequest()} returns true),
+ * then this method returns 1.
*/
- long getPageNumber() throws UnsupportedOperationException;
+ long getPageNumber();
}
diff --git a/src/org/linkeddatafragments/fragments/LinkedDataFragmentRequestBase.java b/src/org/linkeddatafragments/fragments/LinkedDataFragmentRequestBase.java
index d6a7f87..d8e5d25 100644
--- a/src/org/linkeddatafragments/fragments/LinkedDataFragmentRequestBase.java
+++ b/src/org/linkeddatafragments/fragments/LinkedDataFragmentRequestBase.java
@@ -21,7 +21,7 @@ public LinkedDataFragmentRequestBase( final String fragmentURL,
this.fragmentURL = fragmentURL;
this.datasetURL = datasetURL;
this.pageNumberWasRequested = pageNumberWasRequested;
- this.pageNumber = pageNumber;
+ this.pageNumber = (pageNumberWasRequested) ? pageNumber : 1L;
}
@Override
@@ -40,11 +40,8 @@ public boolean isPageRequest() {
}
@Override
- public long getPageNumber() throws UnsupportedOperationException {
- if ( pageNumberWasRequested )
- return pageNumber;
- else
- throw new UnsupportedOperationException();
+ public long getPageNumber() {
+ return pageNumber;
}
@Override
diff --git a/src/org/linkeddatafragments/fragments/tpf/TPFRequestParser.java b/src/org/linkeddatafragments/fragments/tpf/TPFRequestParser.java
index 2449b26..b03e7c8 100644
--- a/src/org/linkeddatafragments/fragments/tpf/TPFRequestParser.java
+++ b/src/org/linkeddatafragments/fragments/tpf/TPFRequestParser.java
@@ -6,14 +6,27 @@
import org.linkeddatafragments.fragments.FragmentRequestParserBase;
import org.linkeddatafragments.fragments.IFragmentRequestParser;
import org.linkeddatafragments.fragments.LinkedDataFragmentRequest;
+import org.linkeddatafragments.util.TriplePatternElementParser;
/**
* An {@link IFragmentRequestParser} for {@link TriplePatternFragmentRequest}s.
*
+ * @param type for representing RDF terms in triple patterns
+ * @param type for representing specific variables in triple patterns
+ *
* @author Olaf Hartig
*/
-public class TPFRequestParser extends FragmentRequestParserBase
+public class TPFRequestParser
+ extends FragmentRequestParserBase
{
+ public final TriplePatternElementParser elmtParser;
+
+ public TPFRequestParser(
+ final TriplePatternElementParser elmtParser )
+ {
+ this.elmtParser = elmtParser;
+ }
+
@Override
protected Worker getWorker( final HttpServletRequest httpRequest,
final ConfigReader config )
@@ -22,8 +35,7 @@ protected Worker getWorker( final HttpServletRequest httpRequest,
return new Worker( httpRequest, config );
}
-
- static protected class Worker extends FragmentRequestParserBase.Worker
+ protected class Worker extends FragmentRequestParserBase.Worker
{
public Worker( final HttpServletRequest request,
final ConfigReader config )
@@ -31,10 +43,12 @@ public Worker( final HttpServletRequest request,
super( request, config );
}
+ @Override
public LinkedDataFragmentRequest createFragmentRequest()
throws IllegalArgumentException
{
- return new TriplePatternFragmentRequestImpl( getFragmentURL(),
+ return new TriplePatternFragmentRequestImpl(
+ getFragmentURL(),
getDatasetURL(),
pageNumberWasRequested,
pageNumber,
@@ -43,21 +57,28 @@ public LinkedDataFragmentRequest createFragmentRequest()
getObject() );
}
- public String getSubject() {
- return request.getParameter(
+ public TriplePatternElement getSubject() {
+ return getParameterAsTriplePatternElement(
TriplePatternFragmentRequest.PARAMETERNAME_SUBJ );
}
- public String getPredicate() {
- return request.getParameter(
+ public TriplePatternElement getPredicate() {
+ return getParameterAsTriplePatternElement(
TriplePatternFragmentRequest.PARAMETERNAME_PRED );
}
- public String getObject() {
- return request.getParameter(
+ public TriplePatternElement getObject() {
+ return getParameterAsTriplePatternElement(
TriplePatternFragmentRequest.PARAMETERNAME_OBJ );
}
+ public TriplePatternElement
+ getParameterAsTriplePatternElement( final String paramName )
+ {
+ final String parameter = request.getParameter( paramName );
+ return elmtParser.parseIntoTriplePatternElement( parameter );
+ }
+
} // end of class Worker
}
diff --git a/src/org/linkeddatafragments/fragments/tpf/TPFRequestParserForJenaBackends.java b/src/org/linkeddatafragments/fragments/tpf/TPFRequestParserForJenaBackends.java
new file mode 100644
index 0000000..ee5f707
--- /dev/null
+++ b/src/org/linkeddatafragments/fragments/tpf/TPFRequestParserForJenaBackends.java
@@ -0,0 +1,29 @@
+package org.linkeddatafragments.fragments.tpf;
+
+import com.hp.hpl.jena.rdf.model.RDFNode;
+
+import org.linkeddatafragments.util.TriplePatternElementParserForJena;
+
+/**
+ * An {@link TPFRequestParser} for Jena-based backends.
+ *
+ * @author Olaf Hartig
+ */
+public class TPFRequestParserForJenaBackends
+ extends TPFRequestParser
+{
+ private static TPFRequestParserForJenaBackends instance = null;
+
+ public static TPFRequestParserForJenaBackends getInstance()
+ {
+ if ( instance == null ) {
+ instance = new TPFRequestParserForJenaBackends();
+ }
+ return instance;
+ }
+
+ protected TPFRequestParserForJenaBackends()
+ {
+ super( TriplePatternElementParserForJena.getInstance() );
+ }
+}
diff --git a/src/org/linkeddatafragments/fragments/tpf/TriplePatternElement.java b/src/org/linkeddatafragments/fragments/tpf/TriplePatternElement.java
new file mode 100644
index 0000000..55f0163
--- /dev/null
+++ b/src/org/linkeddatafragments/fragments/tpf/TriplePatternElement.java
@@ -0,0 +1,49 @@
+package org.linkeddatafragments.fragments.tpf;
+
+/**
+ * Represents an element of a triple pattern (i.e., subject, predicate, object).
+ *
+ * @param type for representing RDF terms in triple patterns
+ * @param type for representing specific variables in triple patterns
+ *
+ * @author Olaf Hartig
+ */
+public interface TriplePatternElement
+{
+ /**
+ * Returns true if this element is a variable (named or unnamed).
+ */
+ boolean isVariable();
+
+ /**
+ * Returns true if this element is a specific variable, and false if either
+ * it is not a variable but an RDF term or it is some variable that is not
+ * specified. The latter (unspecified variables) is possible because when
+ * a client requests a triple pattern fragment, it may omit triple pattern
+ * related parameters.
+ *
+ * If this element is a specific variable (that is, this method returns
+ * true), this specific variable can be obtained by {@link #asVariable()}.
+ */
+ boolean isSpecificVariable();
+
+ /**
+ * Returns a representation of this element as a specific variable (assuming
+ * it is a specific variable).
+ *
+ * @throws UnsupportedOperationException
+ * If this element is not a specific variable (i.e.,
+ * if {@link #isSpecificVariable()} returns false).
+ */
+ VarType asVariable() throws UnsupportedOperationException;
+
+ /**
+ * Returns a representation of this element as an RDF term (assuming it is
+ * an RDF term and not a variable).
+ *
+ * @throws UnsupportedOperationException
+ * If this element is not an RDF term but a variable
+ * (i.e., if {@link #isVariable()} returns true).
+ */
+ TermType asTerm() throws UnsupportedOperationException;
+}
diff --git a/src/org/linkeddatafragments/fragments/tpf/TriplePatternElementFactory.java b/src/org/linkeddatafragments/fragments/tpf/TriplePatternElementFactory.java
new file mode 100644
index 0000000..aff548c
--- /dev/null
+++ b/src/org/linkeddatafragments/fragments/tpf/TriplePatternElementFactory.java
@@ -0,0 +1,63 @@
+package org.linkeddatafragments.fragments.tpf;
+
+/**
+ * A factory for {@link TriplePatternElement}s.
+ *
+ * @param type for representing RDF terms in triple patterns
+ * @param type for representing specific variables in triple patterns
+ *
+ * @author Olaf Hartig
+ */
+public class TriplePatternElementFactory
+{
+ public TriplePatternElement createUnspecifiedVariable() {
+ return new UnspecifiedVariable();
+ }
+
+ public TriplePatternElement createSpecificVariable(
+ final VarType variable ) {
+ return new SpecificVariable( variable );
+ }
+ public TriplePatternElement createRDFTerm(
+ final TermType term ) {
+ return new RDFTerm( term );
+ }
+
+ static abstract public class Variable
+ implements TriplePatternElement
+ {
+ public boolean isVariable() { return true; }
+ public TermType asTerm() { throw new UnsupportedOperationException(); }
+ }
+
+ static public class UnspecifiedVariable
+ extends Variable
+ {
+ public boolean isSpecificVariable() { return false; }
+ public VarType asVariable() { throw new UnsupportedOperationException(); }
+ public String toString() { return "UnspecifiedVariable"; }
+ }
+
+ static public class SpecificVariable
+ extends Variable
+ {
+ protected final VarType v;
+ public SpecificVariable( final VarType variable ) { v = variable; }
+ public boolean isSpecificVariable() { return true; }
+ public VarType asVariable() { return v; }
+ public String toString() { return "SpecificVariable(" + v.toString() + ")"; }
+ }
+
+ static public class RDFTerm
+ implements TriplePatternElement
+ {
+ protected final TermType t;
+ public RDFTerm( final TermType term ) { t = term; }
+ public boolean isVariable() { return false; }
+ public boolean isSpecificVariable() { return false; }
+ public VarType asVariable() { throw new UnsupportedOperationException(); }
+ public TermType asTerm() { return t; }
+ public String toString() { return "RDFTerm(" + t.toString() + ")(type: " + t.getClass().getSimpleName() + ")"; }
+ }
+
+}
diff --git a/src/org/linkeddatafragments/fragments/tpf/TriplePatternFragmentRequest.java b/src/org/linkeddatafragments/fragments/tpf/TriplePatternFragmentRequest.java
index d64f8fe..85154f7 100644
--- a/src/org/linkeddatafragments/fragments/tpf/TriplePatternFragmentRequest.java
+++ b/src/org/linkeddatafragments/fragments/tpf/TriplePatternFragmentRequest.java
@@ -5,32 +5,30 @@
/**
* Represents a request of a Triple Pattern Fragment (TPF).
*
+ * @param type for representing RDF terms in triple patterns
+ * @param type for representing specific variables in triple patterns
+ *
* @author Olaf Hartig
*/
-public interface TriplePatternFragmentRequest extends LinkedDataFragmentRequest
+public interface TriplePatternFragmentRequest
+ extends LinkedDataFragmentRequest
{
public final static String PARAMETERNAME_SUBJ = "subject";
public final static String PARAMETERNAME_PRED = "predicate";
public final static String PARAMETERNAME_OBJ = "object";
/**
- * Returns the subject position of the requested triple pattern (or null,
- * in which case the requested triple pattern has an unnamed variable as
- * subject).
+ * Returns the subject position of the requested triple pattern.
*/
- String getSubject();
+ TriplePatternElement getSubject();
/**
- * Returns the predicate position of the requested triple pattern (or null,
- * in which case the requested triple pattern has an unnamed variable as
- * predicate).
+ * Returns the predicate position of the requested triple pattern.
*/
- String getPredicate();
+ TriplePatternElement getPredicate();
/**
- * Returns the object position of the requested triple pattern (or null,
- * in which case the requested triple pattern has an unnamed variable as
- * object).
+ * Returns the object position of the requested triple pattern.
*/
- String getObject();
+ TriplePatternElement getObject();
}
diff --git a/src/org/linkeddatafragments/fragments/tpf/TriplePatternFragmentRequestImpl.java b/src/org/linkeddatafragments/fragments/tpf/TriplePatternFragmentRequestImpl.java
index 3e1331f..c968212 100644
--- a/src/org/linkeddatafragments/fragments/tpf/TriplePatternFragmentRequestImpl.java
+++ b/src/org/linkeddatafragments/fragments/tpf/TriplePatternFragmentRequestImpl.java
@@ -7,41 +7,50 @@
*
* @author Olaf Hartig
*/
-public class TriplePatternFragmentRequestImpl
+public class TriplePatternFragmentRequestImpl
extends LinkedDataFragmentRequestBase
- implements TriplePatternFragmentRequest
+ implements TriplePatternFragmentRequest
{
- public final String subject;
- public final String predicate;
- public final String object;
+ public final TriplePatternElement subject;
+ public final TriplePatternElement predicate;
+ public final TriplePatternElement object;
public TriplePatternFragmentRequestImpl( final String fragmentURL,
final String datasetURL,
final boolean pageNumberWasRequested,
final long pageNumber,
- final String subject,
- final String predicate,
- final String object )
+ final TriplePatternElement subject,
+ final TriplePatternElement predicate,
+ final TriplePatternElement object )
{
super( fragmentURL, datasetURL, pageNumberWasRequested, pageNumber );
+ if ( subject == null )
+ throw new IllegalArgumentException();
+
+ if ( predicate == null )
+ throw new IllegalArgumentException();
+
+ if ( object == null )
+ throw new IllegalArgumentException();
+
this.subject = subject;
this.predicate = predicate;
this.object = object;
}
@Override
- public String getSubject() {
+ public TriplePatternElement getSubject() {
return subject;
}
@Override
- public String getPredicate() {
+ public TriplePatternElement getPredicate() {
return predicate;
}
@Override
- public String getObject() {
+ public TriplePatternElement getObject() {
return object;
}
@@ -50,9 +59,9 @@ public String toString()
{
return "TriplePatternFragmentRequest(" +
"class: " + getClass().getName() +
- ", subject: " + subject +
- ", predicate: " + predicate +
- ", object: " + object +
+ ", subject: " + subject.toString() +
+ ", predicate: " + predicate.toString() +
+ ", object: " + object.toString() +
", fragmentURL: " + fragmentURL +
", isPageRequest: " + pageNumberWasRequested +
", pageNumber: " + pageNumber +
diff --git a/src/org/linkeddatafragments/servlet/LinkedDataFragmentServlet.java b/src/org/linkeddatafragments/servlet/LinkedDataFragmentServlet.java
index 9a16dce..6b4f89b 100644
--- a/src/org/linkeddatafragments/servlet/LinkedDataFragmentServlet.java
+++ b/src/org/linkeddatafragments/servlet/LinkedDataFragmentServlet.java
@@ -20,11 +20,10 @@
import org.apache.jena.riot.RDFLanguages;
import org.linkeddatafragments.config.ConfigReader;
import org.linkeddatafragments.datasource.DataSourceFactory;
+import org.linkeddatafragments.datasource.DataSourceTypesRegistry;
import org.linkeddatafragments.datasource.IDataSource;
-import org.linkeddatafragments.datasource.hdt.HdtDataSourceType;
+import org.linkeddatafragments.datasource.IDataSourceType;
import org.linkeddatafragments.datasource.index.IndexDataSource;
-import org.linkeddatafragments.datasource.tdb.JenaTDBDataSourceType;
-import org.linkeddatafragments.exceptions.DataSourceException;
import org.linkeddatafragments.exceptions.DataSourceNotFoundException;
import org.linkeddatafragments.fragments.FragmentRequestParserBase;
import org.linkeddatafragments.fragments.LinkedDataFragment;
@@ -49,11 +48,6 @@ public class LinkedDataFragmentServlet extends HttpServlet {
private final HashMap dataSources = new HashMap<>();
private final Collection mimeTypes = new ArrayList<>();
- public LinkedDataFragmentServlet() {
- HdtDataSourceType.register();
- JenaTDBDataSourceType.register();
- }
-
private File getConfigFile(ServletConfig config) throws IOException {
String path = config.getServletContext().getRealPath("/");
if (path == null) {
@@ -80,6 +74,13 @@ public void init(ServletConfig servletConfig) throws ServletException {
File configFile = getConfigFile(servletConfig);
config = new ConfigReader(new FileReader(configFile));
+ // register data source types
+ for ( Entry typeEntry : config.getDataSourceTypes().entrySet() ) {
+ DataSourceTypesRegistry.register( typeEntry.getKey(),
+ typeEntry.getValue() );
+ }
+
+ // register data sources
for (Entry dataSource : config.getDataSources().entrySet()) {
dataSources.put(dataSource.getKey(), DataSourceFactory.create(dataSource.getValue()));
}
@@ -89,7 +90,7 @@ public void init(ServletConfig servletConfig) throws ServletException {
mimeTypes.add(Lang.JSONLD.getHeaderString());
mimeTypes.add(Lang.NTRIPLES.getHeaderString());
mimeTypes.add(Lang.RDFXML.getHeaderString());
- } catch (IOException | DataSourceException e) {
+ } catch (Exception e) {
throw new ServletException(e);
}
}
diff --git a/src/org/linkeddatafragments/util/RDFTermParser.java b/src/org/linkeddatafragments/util/RDFTermParser.java
new file mode 100644
index 0000000..ea56524
--- /dev/null
+++ b/src/org/linkeddatafragments/util/RDFTermParser.java
@@ -0,0 +1,75 @@
+package org.linkeddatafragments.util;
+
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * Parses strings (as obtained from HTTP request parameters) into RDF terms.
+ *
+ * @param type for representing RDF terms
+ *
+ * @author Olaf Hartig
+ */
+abstract public class RDFTermParser
+{
+ public static final Pattern STRINGPATTERN
+ = Pattern.compile("^\"(.*)\"(?:@(.*)|\\^\\^([^<>]*)>?)?$");
+
+ public TermType parseIntoRDFNode( final String param )
+ {
+ if ( param == null || param.isEmpty() )
+ return handleUnparsableParameter( param );
+
+ // identify the kind of RDF term based on the first character
+ char firstChar = param.charAt(0);
+ switch ( firstChar )
+ {
+ // blank node
+ case '_':
+ return createBlankNode( param );
+
+ // angular brackets indicate a URI
+ case '<':
+ return createURI( param.substring(1, param.length()-1) );
+
+ // quotes indicate a string
+ case '"':
+ Matcher matcher = STRINGPATTERN.matcher( param );
+ if ( matcher.matches() ) {
+ String label = matcher.group(1);
+ String langTag = matcher.group(2);
+ String typeURI = matcher.group(3);
+
+ if ( langTag != null )
+ return createLanguageLiteral( label, langTag );
+
+ else if ( typeURI != null )
+ return createTypedLiteral( label, typeURI );
+
+ else
+ return createPlainLiteral( label );
+ }
+ else
+ return handleUnparsableParameter( param );
+
+ // assume it is a URI without angular brackets
+ default:
+ return createURI( param );
+ }
+ }
+
+ abstract public TermType createBlankNode( final String label );
+
+ abstract public TermType createURI( final String uri );
+
+ abstract public TermType createTypedLiteral( final String label,
+ final String typeURI );
+
+ abstract public TermType createLanguageLiteral( final String label,
+ final String langTag );
+
+ abstract public TermType createPlainLiteral( final String label );
+
+ abstract public TermType handleUnparsableParameter( final String param );
+
+}
diff --git a/src/org/linkeddatafragments/util/TriplePatternElementParser.java b/src/org/linkeddatafragments/util/TriplePatternElementParser.java
new file mode 100644
index 0000000..ecbb2e3
--- /dev/null
+++ b/src/org/linkeddatafragments/util/TriplePatternElementParser.java
@@ -0,0 +1,54 @@
+package org.linkeddatafragments.util;
+
+import org.linkeddatafragments.fragments.tpf.TriplePatternElement;
+import org.linkeddatafragments.fragments.tpf.TriplePatternElementFactory;
+
+/**
+ * Parses strings (as obtained from HTTP request parameters) into
+ * {@link TriplePatternElement}s.
+ *
+ * @param type for representing RDF terms
+ * @param type for representing specific variables
+ *
+ * @author Olaf Hartig
+ * @author Ruben Verborgh
+ */
+abstract public class TriplePatternElementParser
+ extends RDFTermParser
+{
+ public final TriplePatternElementFactory factory =
+ new TriplePatternElementFactory();
+
+ public TriplePatternElement
+ parseIntoTriplePatternElement( final String param )
+ {
+ // nothing or empty indicates an unspecified variable
+ if ( param == null || param.isEmpty() )
+ return factory.createUnspecifiedVariable();
+
+ // identify the kind of RDF term based on the first character
+ char firstChar = param.charAt(0);
+ switch ( firstChar )
+ {
+ // specific variable
+ case '?':
+ {
+ final String varName = param.substring(1);
+ final VarType var = createSpecificVariable( varName );
+ return factory.createSpecificVariable( var );
+ }
+
+ // blank node indicates an unspecified variable
+ case '_':
+ {
+ return factory.createUnspecifiedVariable();
+ }
+
+ // assume it is an RDF term
+ default:
+ return factory.createRDFTerm( parseIntoRDFNode(param) );
+ }
+ }
+
+ abstract public VarType createSpecificVariable( final String varName );
+}
diff --git a/src/org/linkeddatafragments/util/TriplePatternElementParserForJena.java b/src/org/linkeddatafragments/util/TriplePatternElementParserForJena.java
new file mode 100644
index 0000000..41dd83c
--- /dev/null
+++ b/src/org/linkeddatafragments/util/TriplePatternElementParserForJena.java
@@ -0,0 +1,73 @@
+package org.linkeddatafragments.util;
+
+import com.hp.hpl.jena.datatypes.RDFDatatype;
+import com.hp.hpl.jena.datatypes.TypeMapper;
+import com.hp.hpl.jena.rdf.model.RDFNode;
+import com.hp.hpl.jena.rdf.model.ResourceFactory;
+
+/**
+ * A {@link TriplePatternElementParser} for Jena-based backends.
+ *
+ * @author Olaf Hartig
+ */
+public class TriplePatternElementParserForJena
+ extends TriplePatternElementParser
+{
+ private static TriplePatternElementParserForJena instance = null;
+
+ public static TriplePatternElementParserForJena getInstance()
+ {
+ if ( instance == null ) {
+ instance = new TriplePatternElementParserForJena();
+ }
+ return instance;
+ }
+
+ protected TriplePatternElementParserForJena() {}
+
+ @Override
+ public String createSpecificVariable( final String varName )
+ {
+ return varName;
+ }
+
+ @Override
+ public RDFNode createBlankNode( final String label )
+ {
+ return ResourceFactory.createResource();
+ }
+
+ @Override
+ public RDFNode createURI( final String uri )
+ {
+ return ResourceFactory.createResource( uri );
+ }
+
+ @Override
+ public RDFNode createTypedLiteral( final String label,
+ final String typeURI )
+ {
+ final RDFDatatype dt = TypeMapper.getInstance()
+ .getSafeTypeByName( typeURI );
+ return ResourceFactory.createTypedLiteral( label, dt );
+ }
+
+ @Override
+ public RDFNode createLanguageLiteral( final String label,
+ final String languageTag )
+ {
+ return ResourceFactory.createLangLiteral( label, languageTag );
+ }
+
+ @Override
+ public RDFNode createPlainLiteral( final String label )
+ {
+ return ResourceFactory.createPlainLiteral( label );
+ }
+
+ @Override
+ public RDFNode handleUnparsableParameter( final String parameter )
+ {
+ return CommonResources.INVALID_URI;
+ }
+}
diff --git a/src/test/java/org/linkeddatafragments/datasource/DataSourceTest.java b/src/test/java/org/linkeddatafragments/datasource/DataSourceTest.java
index 489611a..81c3589 100644
--- a/src/test/java/org/linkeddatafragments/datasource/DataSourceTest.java
+++ b/src/test/java/org/linkeddatafragments/datasource/DataSourceTest.java
@@ -15,15 +15,18 @@
import org.linkeddatafragments.datasource.IDataSource;
import org.linkeddatafragments.datasource.IFragmentRequestProcessor;
import org.linkeddatafragments.fragments.LinkedDataFragment;
+import org.linkeddatafragments.fragments.tpf.TriplePatternElement;
import org.linkeddatafragments.fragments.tpf.TriplePatternFragment;
import org.linkeddatafragments.fragments.tpf.TriplePatternFragmentRequest;
+import org.linkeddatafragments.fragments.tpf.TriplePatternFragmentRequestImpl;
+import org.linkeddatafragments.util.TriplePatternElementParser;
/**
*
* @author Bart Hanssens
*/
-public abstract class DataSourceTest {
+public abstract class DataSourceTest {
private static IDataSource ds;
/**
@@ -43,6 +46,9 @@ public static IDataSource getDatasource() {
public static void setDatasource(IDataSource ds) {
DataSourceTest.ds = ds;
}
+
+ protected abstract TriplePatternElementParser
+ getTriplePatternElementParser();
/**
* Copy the demo triple in the jar to a temp file.
@@ -84,15 +90,18 @@ public static JsonObject createConfig(String title, String desc, String type) {
*/
@Test
public void testEmpty() {
- TriplePatternFragmentRequest request = new TriplePatternFragmentRequest() {
- public boolean isPageRequest() { return true; }
- public long getPageNumber() { return 1L; }
- public String getFragmentURL() { return "http://example.org/f"; }
- public String getDatasetURL() { return "http://example.org/"; }
- public String getSubject() { return "http://nothing.ldf.org"; }
- public String getPredicate() { return null; }
- public String getObject() { return null; }
- };
+ final TriplePatternElementParser tpeParser =
+ getTriplePatternElementParser();
+
+ final TriplePatternFragmentRequest request =
+ new TriplePatternFragmentRequestImpl(
+ "http://example.org/f", // fragmentURL
+ "http://example.org/", // datasetURL,
+ true, // pageNumberWasRequested,
+ 1L, //pageNumber,
+ tpeParser.parseIntoTriplePatternElement("http://nothing.ldf.org"), // subject,
+ tpeParser.parseIntoTriplePatternElement(null), // predicate,
+ tpeParser.parseIntoTriplePatternElement(null) ); //object
final IFragmentRequestProcessor proc = getDatasource().getRequestProcessor();
final LinkedDataFragment ldf = proc.createRequestedFragment( request );
@@ -109,14 +118,25 @@ public void testEmpty() {
*/
@Test
public void testEstimate() {
- TriplePatternFragmentRequest request = new TriplePatternFragmentRequest() {
+ final TriplePatternElementParser tpeParser =
+ getTriplePatternElementParser();
+
+ final TriplePatternFragmentRequest request =
+ new TriplePatternFragmentRequest() {
public boolean isPageRequest() { return true; }
public long getPageNumber() { return 1L; }
public String getFragmentURL() { return "http://example.org/f"; }
public String getDatasetURL() { return "http://example.org/"; }
- public String getSubject() { return "http://data.gov.be/catalog/ckanvl"; }
- public String getPredicate() { return null; }
- public String getObject() { return null; }
+
+ public TriplePatternElement getSubject() {
+ return tpeParser.parseIntoTriplePatternElement("http://data.gov.be/catalog/ckanvl");
+ }
+ public TriplePatternElement getPredicate() {
+ return tpeParser.parseIntoTriplePatternElement(null);
+ }
+ public TriplePatternElement getObject() {
+ return tpeParser.parseIntoTriplePatternElement(null);
+ }
};
final IFragmentRequestProcessor proc = getDatasource().getRequestProcessor();
diff --git a/src/test/java/org/linkeddatafragments/datasource/HdtDataSourceTest.java b/src/test/java/org/linkeddatafragments/datasource/HdtDataSourceTest.java
index f9f7ef4..09e5e2c 100644
--- a/src/test/java/org/linkeddatafragments/datasource/HdtDataSourceTest.java
+++ b/src/test/java/org/linkeddatafragments/datasource/HdtDataSourceTest.java
@@ -1,13 +1,18 @@
package test.java.org.linkeddatafragments.datasource;
import com.google.gson.JsonObject;
+import com.hp.hpl.jena.rdf.model.RDFNode;
+
import java.io.File;
import org.junit.After;
import org.junit.AfterClass;
import org.junit.Before;
import org.junit.BeforeClass;
import org.linkeddatafragments.datasource.DataSourceFactory;
+import org.linkeddatafragments.datasource.DataSourceTypesRegistry;
import org.linkeddatafragments.datasource.hdt.HdtDataSourceType;
+import org.linkeddatafragments.util.TriplePatternElementParser;
+import org.linkeddatafragments.util.TriplePatternElementParserForJena;
import org.rdfhdt.hdt.enums.RDFNotation;
import org.rdfhdt.hdt.hdt.HDT;
import org.rdfhdt.hdt.hdt.HDTManager;
@@ -17,14 +22,24 @@
*
* @author Bart Hanssens
*/
-public class HdtDataSourceTest extends DataSourceTest {
+public class HdtDataSourceTest extends DataSourceTest {
private static File hdtfile;
+ @Override
+ protected TriplePatternElementParser
+ getTriplePatternElementParser()
+ {
+ return TriplePatternElementParserForJena.getInstance();
+ }
@BeforeClass
public static void setUpClass() throws Exception {
- HdtDataSourceType.register();
+ final String typeName = "HdtTestSourceType";
+ if ( ! DataSourceTypesRegistry.isRegistered(typeName) ) {
+ DataSourceTypesRegistry.register( typeName, new HdtDataSourceType() );
+ }
+
// HDT does not seem to support an InputReader, so write to temp file
File temp = getResourceAsFile();
@@ -37,8 +52,7 @@ public static void setUpClass() throws Exception {
temp.getAbsoluteFile().delete();
// Everything is in place, now create the LDF datasource
- JsonObject config = createConfig("hdt test", "hdt test",
- HdtDataSourceType.TYPE_NAME);
+ JsonObject config = createConfig("hdt test", "hdt test", typeName);
JsonObject settings = new JsonObject();
settings.addProperty("file", hdtfile.getAbsolutePath());
diff --git a/src/test/java/org/linkeddatafragments/datasource/JenaTDBDataSourceTest.java b/src/test/java/org/linkeddatafragments/datasource/JenaTDBDataSourceTest.java
index 00548b3..9ee6935 100644
--- a/src/test/java/org/linkeddatafragments/datasource/JenaTDBDataSourceTest.java
+++ b/src/test/java/org/linkeddatafragments/datasource/JenaTDBDataSourceTest.java
@@ -4,6 +4,7 @@
import com.hp.hpl.jena.query.Dataset;
import com.hp.hpl.jena.rdf.model.Model;
+import com.hp.hpl.jena.rdf.model.RDFNode;
import com.hp.hpl.jena.tdb.TDBFactory;
import java.io.File;
@@ -19,19 +20,33 @@
import org.junit.BeforeClass;
import org.linkeddatafragments.datasource.DataSourceFactory;
+import org.linkeddatafragments.datasource.DataSourceTypesRegistry;
import org.linkeddatafragments.datasource.tdb.JenaTDBDataSourceType;
+import org.linkeddatafragments.util.TriplePatternElementParser;
+import org.linkeddatafragments.util.TriplePatternElementParserForJena;
/**
*
* @author Bart Hanssens
*/
-public class JenaTDBDataSourceTest extends DataSourceTest {
+public class JenaTDBDataSourceTest extends DataSourceTest {
private static File jena;
private static Dataset dataset;
+
+ @Override
+ protected TriplePatternElementParser
+ getTriplePatternElementParser()
+ {
+ return TriplePatternElementParserForJena.getInstance();
+ }
@BeforeClass
public static void setUpClass() throws Exception {
- JenaTDBDataSourceType.register();
+ final String typeName = "JenaSourceType";
+ if ( ! DataSourceTypesRegistry.isRegistered(typeName) ) {
+ DataSourceTypesRegistry.register( typeName,
+ new JenaTDBDataSourceType() );
+ }
String tmpdir = System.getProperty("java.io.tmpdir");
jena = new File(tmpdir, "ldf-jena-test");
@@ -46,7 +61,7 @@ public static void setUpClass() throws Exception {
// Everything is in place, now create the LDF datasource
JsonObject config = createConfig("jena tdb test", "jena tdb test",
- JenaTDBDataSourceType.TYPE_NAME);
+ typeName);
JsonObject settings = new JsonObject();
settings.addProperty("directory", jena.getAbsolutePath());