All Downloads are FREE. Search and download functionalities are using the official Maven repository.

eu.fbk.knowledgestore.Operation Maven / Gradle / Ivy

Go to download

The Core module (ks-core) contains core abstractions and basic functionalities shared by the KnowledgeStore Frontend Server and the Java Client. It also defines the Java version of the KnowledgeStore API.

There is a newer version: 1.7.1
Show newest version
package eu.fbk.knowledgestore;

import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import javax.annotation.Nullable;

import com.google.common.base.MoreObjects;
import com.google.common.base.Preconditions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Iterables;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
import com.google.common.collect.Sets;

import org.openrdf.model.Statement;
import org.openrdf.model.URI;
import org.openrdf.query.BindingSet;

import eu.fbk.knowledgestore.Outcome.Status;
import eu.fbk.knowledgestore.data.Criteria;
import eu.fbk.knowledgestore.data.Data;
import eu.fbk.knowledgestore.data.Handler;
import eu.fbk.knowledgestore.data.ParseException;
import eu.fbk.knowledgestore.data.Record;
import eu.fbk.knowledgestore.data.Representation;
import eu.fbk.knowledgestore.data.Stream;
import eu.fbk.knowledgestore.data.XPath;
import eu.fbk.knowledgestore.vocabulary.KS;

// best effort basis: will interrupt thread, then it is up to the implementation to
// decide what to do
// for read operations, HTTP connection can be interrupted; for create / merge
// operations, no more data should be sent; delete / update will make the connection
// fail

// properties for which there is no criteria are not modified

public abstract class Operation {

    private final Map inheritedNamespaces;

    Map namespaces;

    Long timeout;

    Operation(@Nullable final Map inheritedNamespaces) {
        this.inheritedNamespaces = inheritedNamespaces != null ? inheritedNamespaces
                : ImmutableMap.of();
        this.namespaces = this.inheritedNamespaces;
        this.timeout = null;
    }

    /**
     * Sets the optional timeout for this operation in milliseconds. Passing null or a
     * non-positive value will remove any timeout previously set.
     * 
     * @param timeout
     *            the timeout; null or non-positive to reset
     * @return this operation object for call chaining
     */
    public synchronized Operation timeout(@Nullable final Long timeout) {
        this.timeout = timeout == null || timeout > 0 ? timeout : null;
        return this;
    }

    /**
     * Sets the optional namespaces for this operation. Supplied namespaces overrides the
     * namespaces inherited from the {@code Session}. Passing null will remove any namespace map
     * previously set on the operation.
     * 
     * @param namespaces
     *            the namespace map overriding session namespaces; null to reset
     * @return this operation object for call chaining
     */
    public synchronized Operation namespaces(@Nullable final Map namespaces) {
        this.namespaces = namespaces == null ? this.inheritedNamespaces : Data.newNamespaceMap(
                namespaces, this.inheritedNamespaces);
        return this;
    }

    // UTILITY METHODS

    static URI checkType(final URI type) {
        Preconditions.checkNotNull(type, "No type specified");
        if (!type.equals(KS.RESOURCE) && !type.equals(KS.MENTION) && !type.equals(KS.ENTITY)
                && !type.equals(KS.AXIOM)) {
            throw new IllegalArgumentException("Invalid type: " + type);
        }
        return type;
    }

    static XPath conditionFor(final URI property, final Object... allowedValues) {
        final String namespace = property.getNamespace();
        final String prefix = MoreObjects.firstNonNull(//
                Data.namespaceToPrefix(namespace, Data.getNamespaceMap()), "ns");
        return XPath.parse(
                String.format("with %s: <%s> : %s:%s = {}", prefix, namespace, prefix,
                        property.getLocalName()), allowedValues);
    }

    static  Handler handlerFor(@Nullable final Collection collection) {
        if (collection == null) {
            return null;
        } else {
            return new Handler() {

                @Override
                public void handle(final T element) {
                    if (element != null) {
                        collection.add(element);
                    }
                }

            };
        }
    }

    @Nullable
    static XPath merge(final Iterable conditions) {
        return conditions == null || Iterables.isEmpty(conditions) ? null : XPath.compose("and",
                (Object[]) Iterables.toArray(conditions, XPath.class));
    }

    static  List add(@Nullable final List list, final T element) {
        if (list == null) {
            return ImmutableList.of(element);
        } else {
            final List tmp = Lists.newArrayList(list);
            tmp.add(element);
            return ImmutableList.copyOf(tmp);
        }
    }

    static  Set add(@Nullable final Set set, final T element) {
        if (set == null) {
            return ImmutableSet.of(element);
        } else {
            final List tmp = Lists.newArrayList(set);
            tmp.add(element);
            return ImmutableSet.copyOf(tmp);
        }
    }

    /**
     * Download operation.
     * 

* This operation attempts at fetching the representation associated to a resource with the ID * specified. The operation is controlled by two optional parameters: * {@link #caching(boolean)} enables or disables the use of intermediate caches, if available, * while {@link #accept(String...)} / {@link #accept(Iterable)} require the returned * representation to have a certain MIME type (if it is not the case, the invocation fails). *

*/ public abstract static class Download extends Operation { private final URI resourceID; @Nullable private Set mimeTypes; private boolean caching; /** * Creates a new {@code Download} operation instance. * * @param inheritedNamespaces * an immutable map of inherited namespaces, possibly null * @param resourceID * the ID of the resource whose representation should be retrieved */ protected Download(@Nullable final Map inheritedNamespaces, final URI resourceID) { super(inheritedNamespaces); this.resourceID = Preconditions.checkNotNull(resourceID, "Null resource ID"); this.mimeTypes = null; this.caching = true; } @Override public Download timeout(@Nullable final Long timeout) { return (Download) super.timeout(timeout); } @Override public Download namespaces(@Nullable final Map namespaces) { return (Download) super.namespaces(namespaces); } /** * Sets the acceptable MIME type (default: accept everything). Supplied values override * previously configured MIME types; passing null will drop the constraint. * * @param mimeTypes * the acceptable MIME types; null (default) to drop any constraint * @return this operation object, for call chaining */ public final synchronized Download accept(@Nullable final String... mimeTypes) { return accept(mimeTypes == null ? null : Arrays.asList(mimeTypes)); } /** * Sets the acceptable MIME types (default: accept everything). Supplied values override * previously configured MIME types; passing null will drop the constraint. * * @param mimeTypes * the acceptable MIME types; null (default) to drop any constraint * @return this operation object, for call chaining */ public final synchronized Download accept( @Nullable final Iterable mimeTypes) { this.mimeTypes = mimeTypes == null ? null : Sets.newLinkedHashSet(mimeTypes); return this; } /** * Sets whether the representation can be retrieved from caches (default true). * * @param caching * true, if the representation can be retrieved from caches * @return this operation object, for call chaining */ public final synchronized Download caching(final boolean caching) { this.caching = caching; return this; } /** * Executes the operation, returning the requested representation or null, if it does not * exist. Note that returned representations MUST be closed after use. * * @return the requested representation, or null if it does not exist * @throws OperationException * in case of failure (see possible outcome status codes) */ public final synchronized Representation exec() throws OperationException { return doExec(this.timeout, this.resourceID, this.mimeTypes, this.caching); } /** * Implementation method responsible of executing the download operation. * * @param timeout * the optional timeout for the operation; null if there is no timeout * @param resourceID * the ID of the resource whose representation must be returned * @param mimeTypes * the acceptable MIME types; null if there is no constraint * @param caching * true, if the representation can be retrieved from a cache * @return the resource representation, if exists, otherwise null * @throws OperationException * in case of failure (see possible outcome status codes) */ @Nullable protected abstract Representation doExec(@Nullable Long timeout, URI resourceID, @Nullable Set mimeTypes, boolean caching) throws OperationException; } /** * Upload operation. *

* The operation outcome may assume one among the following {@code Status} codes: *

* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
StatusExplanationReported as *
{@link Status#OK_CREATED}the file was successfully uploaded without replacing an existing file for the same * resource{@code exec()} return value
{@link Status#OK_MODIFIED}the file was successfully uploaded and replaced an existing file bound to the same * resource{@code exec()} return value
{@link Status#OK_DELETED}the file was successfully deleted{@code exec()} return value
{@link Status#ERROR_INVALID_INPUT}in case the supplied URI is not valid or a problem is detected in the uploaded file{@code OperationException}
{@link Status#ERROR_OBJECT_NOT_FOUND}the file cannot be deleted because it does not exist (the associated resource may or * may not exist){@code OperationException}
{@link Status#ERROR_DEPENDENCY_NOT_FOUND}supplied file cannot be stored because the referenced resource does not exist{@code OperationException}
{@link Status#ERROR_UNEXPECTED}an unexpected error occurred preventing the successful execution of the operation{@code OperationException}
{@link Status#ERROR_UNKNOWN}a connectivity problem caused the interruption of the operation whose outcome is * unknown{@code OperationException}
*/ public abstract static class Upload extends Operation { private final URI resourceID; @Nullable private Representation representation; protected Upload(final Map inheritedNamespaces, final URI id) { super(inheritedNamespaces); this.resourceID = Preconditions.checkNotNull(id, "Null resource ID"); this.representation = null; } @Override public Upload timeout(@Nullable final Long timeout) { return (Upload) super.timeout(timeout); } @Override public Upload namespaces(@Nullable final Map namespaces) { return (Upload) super.namespaces(namespaces); } /** * Sets the representation to upload, if any. Setting null will cause any previously * stored representation to be dropped. * * @param representation * the representation to upload, if any * @return this operation object, for call chaining */ public final synchronized Upload representation( @Nullable final Representation representation) { this.representation = representation; return this; } /** * Executes the operation, returning its outcome. * * @return the outcome of the operation * @throws OperationException * in case of failure (see possible outcome status codes) */ public final synchronized Outcome exec() throws OperationException { return doExec(this.timeout, this.resourceID, this.representation); } protected abstract Outcome doExec(@Nullable Long timeout, URI resourceID, @Nullable Representation representation) throws OperationException; } public abstract static class Count extends Operation { private final URI type; @Nullable private List conditions; @Nullable private Set ids; protected Count(final Map namespaces, final URI type) { super(namespaces); this.type = checkType(type); this.conditions = null; this.ids = null; } @Override public Count timeout(@Nullable final Long timeout) { return (Count) super.timeout(timeout); } @Override public Count namespaces(@Nullable final Map namespaces) { return (Count) super.namespaces(namespaces); } public final synchronized Count conditions(@Nullable final XPath... conditions) { return conditions(conditions == null ? null : Arrays.asList(conditions)); } public final synchronized Count conditions( @Nullable final Iterable conditions) { this.conditions = conditions == null ? null : ImmutableList.copyOf(conditions); return this; } public final synchronized Count condition(final String condition, final Object... arguments) throws ParseException { this.conditions = add(this.conditions, XPath.parse(this.namespaces, condition, arguments)); return this; } public final synchronized Count condition(final URI property, final Object... allowedValues) { this.conditions = add(this.conditions, conditionFor(property, allowedValues)); return this; } public final synchronized Count ids(@Nullable final URI... ids) { return ids(ids == null ? null : Arrays.asList(ids)); } public final synchronized Count ids(@Nullable final Iterable ids) { this.ids = ids == null ? null : ImmutableSet.copyOf(ids); return this; } public final synchronized long exec() throws OperationException { return doExec(this.timeout, this.type, merge(this.conditions), this.ids); } protected abstract long doExec(@Nullable Long timeout, URI type, @Nullable XPath condition, @Nullable Set ids) throws OperationException; } public abstract static class Retrieve extends Operation { private final URI type; @Nullable private List conditions; @Nullable private Set ids; @Nullable private Set properties; @Nullable private Long offset; @Nullable private Long limit; protected Retrieve(final Map namespaces, final URI type) { super(namespaces); this.type = checkType(type); this.conditions = null; this.ids = null; this.properties = null; this.offset = null; this.limit = null; } @Override public Retrieve timeout(@Nullable final Long timeout) { return (Retrieve) super.timeout(timeout); } @Override public Retrieve namespaces(@Nullable final Map namespaces) { return (Retrieve) super.namespaces(namespaces); } public final synchronized Retrieve conditions(@Nullable final XPath... conditions) { return conditions(conditions == null ? null : Arrays.asList(conditions)); } public final synchronized Retrieve conditions( @Nullable final Iterable conditions) { this.conditions = conditions == null ? null : ImmutableList.copyOf(conditions); return this; } public final synchronized Retrieve condition(final String condition, final Object... arguments) throws ParseException { this.conditions = add(this.conditions, XPath.parse(this.namespaces, condition, arguments)); return this; } public final synchronized Retrieve condition(final URI property, final Object... allowedValues) { this.conditions = add(this.conditions, conditionFor(property, allowedValues)); return this; } public final synchronized Retrieve ids(@Nullable final URI... ids) { return ids(ids == null ? null : Arrays.asList(ids)); } public final synchronized Retrieve ids(@Nullable final Iterable ids) { this.ids = ids == null ? null : ImmutableSet.copyOf(ids); return this; } // no call or empty = all properties public final synchronized Retrieve properties(@Nullable final URI... properties) { return properties(properties == null ? null : Arrays.asList(properties)); } public final synchronized Retrieve properties( @Nullable final Iterable properties) { this.properties = properties == null ? null : ImmutableSet.copyOf(properties); return this; } public final synchronized Retrieve offset(@Nullable final Long offset) { this.offset = offset == null || offset <= 0 ? null : offset; return this; } public final synchronized Retrieve limit(@Nullable final Long limit) { this.limit = limit == null || limit <= 0 ? null : limit; return this; } public final synchronized Stream exec() throws OperationException { return doExec(this.timeout, this.type, merge(this.conditions), this.ids, this.properties, this.offset, this.limit); } protected abstract Stream doExec(@Nullable final Long timeout, final URI type, @Nullable final XPath condition, @Nullable final Set ids, @Nullable final Set properties, @Nullable Long offset, @Nullable Long limit) throws OperationException; } public abstract static class Create extends Operation { private final URI type; @Nullable private Stream records; protected Create(final Map namespaces, final URI type) { super(namespaces); this.type = checkType(type); this.records = null; } @Override public Create timeout(@Nullable final Long timeout) { return (Create) super.timeout(timeout); } @Override public Create namespaces(@Nullable final Map namespaces) { return (Create) super.namespaces(namespaces); } public final synchronized Create records(@Nullable final Record... records) { this.records = records == null ? null : Stream.create(records); return this; } public final synchronized Create records(// @Nullable final Iterable records) { this.records = records == null ? null : Stream.create(records); return this; } public final synchronized Outcome exec() throws OperationException { return doExec(this.timeout, this.type, this.records, null); } // TODO: errors from the handlers are logged and cause the invocation to be interrupted // eventually; still, the handler will be notified of all the outcomes until the // invocation ends public final synchronized Outcome exec(@Nullable final Handler handler) throws OperationException { return doExec(this.timeout, this.type, this.records, handler); } public final synchronized Outcome exec( @Nullable final Collection collection) throws OperationException { return doExec(this.timeout, this.type, this.records, handlerFor(collection)); } protected abstract Outcome doExec(@Nullable Long timeout, final URI type, @Nullable final Stream records, @Nullable final Handler handler) throws OperationException; } public abstract static class Merge extends Operation { private final URI type; @Nullable private Stream records; @Nullable private Criteria criteria; protected Merge(final Map namespaces, final URI type) { super(namespaces); this.type = checkType(type); this.records = null; this.criteria = null; } @Override public Merge timeout(@Nullable final Long timeout) { return (Merge) super.timeout(timeout); } @Override public Merge namespaces(@Nullable final Map namespaces) { return (Merge) super.namespaces(namespaces); } public final synchronized Merge records(@Nullable final Record... records) { this.records = records == null ? null : Stream.create(records); return this; } public final synchronized Merge records(// @Nullable final Iterable records) { this.records = records == null ? null : Stream.create(records); return this; } public final synchronized Merge criteria(@Nullable final Criteria... criteria) { return criteria(criteria == null ? null : Arrays.asList(criteria)); } public final synchronized Merge criteria( @Nullable final Iterable criteria) { this.criteria = criteria == null || Iterables.isEmpty(criteria) ? null : Criteria .compose(Iterables.toArray(criteria, Criteria.class)); return this; } public final synchronized Merge criteria(@Nullable final String criteria) throws ParseException { this.criteria = criteria == null ? null : Criteria.parse(criteria, this.namespaces); return this; } public final synchronized Outcome exec() throws OperationException { return doExec(this.timeout, this.type, this.records, this.criteria, null); } public final synchronized Outcome exec(@Nullable final Handler handler) throws OperationException { return doExec(this.timeout, this.type, this.records, this.criteria, handler); } public final synchronized Outcome exec( @Nullable final Collection collection) throws OperationException { return doExec(this.timeout, this.type, this.records, this.criteria, handlerFor(collection)); } protected abstract Outcome doExec(@Nullable Long timeout, URI type, @Nullable Stream stream, @Nullable Criteria criteria, @Nullable Handler handler) throws OperationException; } public abstract static class Update extends Operation { private final URI type; @Nullable private List conditions; @Nullable private Set ids; @Nullable private Record record; @Nullable private Criteria criteria; protected Update(final Map namespaces, final URI type) { super(namespaces); this.type = checkType(type); this.conditions = null; this.ids = null; this.record = null; this.criteria = null; } @Override public Update timeout(@Nullable final Long timeout) { return (Update) super.timeout(timeout); } @Override public Update namespaces(@Nullable final Map namespaces) { return (Update) super.namespaces(namespaces); } public final synchronized Update conditions(@Nullable final XPath... conditions) { return conditions(conditions == null ? null : Arrays.asList(conditions)); } public final synchronized Update conditions( @Nullable final Iterable conditions) { this.conditions = conditions == null ? null : ImmutableList.copyOf(conditions); return this; } public final synchronized Update condition(final String condition, final Object... arguments) throws ParseException { this.conditions = add(this.conditions, XPath.parse(this.namespaces, condition, arguments)); return this; } public final synchronized Update condition(final URI property, final Object... allowedValues) { this.conditions = add(this.conditions, conditionFor(property, allowedValues)); return this; } public final synchronized Update ids(@Nullable final URI... ids) { return ids(ids == null ? null : Arrays.asList(ids)); } public final synchronized Update ids(@Nullable final Iterable ids) { this.ids = ids == null ? null : ImmutableSet.copyOf(ids); return this; } public final synchronized Update record(@Nullable final Record record) { this.record = record; return this; } public final synchronized Update criteria(@Nullable final Criteria... criteria) { return criteria(criteria == null ? null : Arrays.asList(criteria)); } public final synchronized Update criteria( @Nullable final Iterable criteria) { this.criteria = criteria == null || Iterables.isEmpty(criteria) ? null : Criteria .compose(Iterables.toArray(criteria, Criteria.class)); return this; } public final synchronized Update criteria(@Nullable final String criteria) throws ParseException { this.criteria = criteria == null ? null : Criteria.parse(criteria, this.namespaces); return this; } public final synchronized Outcome exec() throws OperationException { return doExec(this.timeout, this.type, merge(this.conditions), this.ids, this.record, this.criteria, null); } public final synchronized Outcome exec(@Nullable final Handler handler) throws OperationException { final Record record = this.record != null ? this.record : Record.create(); return doExec(this.timeout, this.type, merge(this.conditions), this.ids, record, this.criteria, handler); } public final synchronized Outcome exec( @Nullable final Collection collection) throws OperationException { return doExec(this.timeout, this.type, merge(this.conditions), this.ids, this.record, this.criteria, handlerFor(collection)); } protected abstract Outcome doExec(@Nullable Long timeout, URI type, @Nullable XPath condition, @Nullable Set ids, @Nullable Record record, @Nullable Criteria criteria, @Nullable Handler handler) throws OperationException; } public abstract static class Delete extends Operation { private final URI type; @Nullable private List conditions; @Nullable private Set ids; protected Delete(final Map namespaces, final URI type) { super(namespaces); this.type = checkType(type); this.conditions = null; this.ids = null; } @Override public Delete timeout(@Nullable final Long timeout) { return (Delete) super.timeout(timeout); } @Override public Delete namespaces(@Nullable final Map namespaces) { return (Delete) super.namespaces(namespaces); } public final synchronized Delete conditions(@Nullable final XPath... conditions) { return conditions(conditions == null ? null : Arrays.asList(conditions)); } public final synchronized Delete conditions( @Nullable final Iterable conditions) { this.conditions = conditions == null ? null : ImmutableList.copyOf(conditions); return this; } public final synchronized Delete condition(final String condition, final Object... arguments) throws ParseException { this.conditions = add(this.conditions, XPath.parse(this.namespaces, condition, arguments)); return this; } public final synchronized Delete condition(final URI property, final Object... allowedValues) { this.conditions = add(this.conditions, conditionFor(property, allowedValues)); return this; } public final synchronized Delete ids(@Nullable final URI... ids) { return ids(ids == null ? null : Arrays.asList(ids)); } public final synchronized Delete ids(@Nullable final Iterable ids) { this.ids = ids == null ? null : ImmutableSet.copyOf(ids); return this; } public final synchronized Outcome exec() throws OperationException { return doExec(this.timeout, this.type, merge(this.conditions), this.ids, null); } public final synchronized Outcome exec(@Nullable final Handler handler) throws OperationException { return doExec(this.timeout, this.type, merge(this.conditions), this.ids, handler); } public final synchronized Outcome exec( @Nullable final Collection collection) throws OperationException { return doExec(this.timeout, this.type, merge(this.conditions), this.ids, handlerFor(collection)); } protected abstract Outcome doExec(@Nullable Long timeout, URI type, @Nullable XPath condition, @Nullable Set ids, @Nullable Handler handler) throws OperationException; } public abstract static class Match extends Operation { private static final Map COMPONENT_NORMALIZATION_MAP = // ImmutableMap.builder().put(KS.RESOURCE, KS.RESOURCE) .put(KS.MATCHED_RESOURCE, KS.RESOURCE).put(KS.MENTION, KS.MENTION) .put(KS.MATCHED_MENTION, KS.MENTION).put(KS.ENTITY, KS.ENTITY) .put(KS.MATCHED_ENTITY, KS.ENTITY).build(); private final Map> conditions; private final Map> ids; private final Map> properties; protected Match(final Map namespaces) { super(namespaces); this.conditions = Maps.newHashMap(); this.ids = Maps.newHashMap(); this.properties = Maps.newHashMap(); } @Override public Match timeout(@Nullable final Long timeout) { return (Match) super.timeout(timeout); } @Override public Match namespaces(@Nullable final Map namespaces) { return (Match) super.namespaces(namespaces); } public final synchronized Match conditions(@Nullable final URI component, @Nullable final XPath... conditions) { return conditions(component, conditions == null ? null : Arrays.asList(conditions)); } public final synchronized Match conditions(@Nullable final URI component, @Nullable final Iterable conditions) { if (component == null) { this.conditions.clear(); } else { this.conditions.put(checkComponent(component), conditions == null ? null : ImmutableSet.copyOf(conditions)); } return this; } public final synchronized Match condition(final URI component, final String condition, final Object... arguments) throws ParseException { final URI comp = checkComponent(component); final XPath cond = XPath.parse(this.namespaces, condition, arguments); this.conditions.put(comp, add(this.conditions.get(comp), cond)); return this; } public final synchronized Match condition(final URI component, final URI property, final Object... allowedValues) { final URI comp = checkComponent(component); final XPath cond = conditionFor(property, allowedValues); this.conditions.put(comp, add(this.conditions.get(comp), cond)); return this; } public final synchronized Match ids(@Nullable final URI component, @Nullable final URI... ids) { return ids(component, ids == null ? null : Arrays.asList(ids)); } public final synchronized Match ids(@Nullable final URI component, @Nullable final Iterable ids) { if (component == null) { this.ids.clear(); } else { this.ids.put(checkComponent(component), ids == null ? null : ImmutableSet.copyOf(ids)); } return this; } // no call = exclude class; empty = all properties public final synchronized Match properties(@Nullable final URI component, @Nullable final URI... properties) { return properties(component, properties == null ? null : Arrays.asList(properties)); } public final synchronized Match properties(@Nullable final URI component, @Nullable final Iterable properties) { if (component == null) { this.properties.clear(); } else { this.properties.put(checkComponent(component), properties == null ? null : ImmutableSet.copyOf(properties)); } return this; } public final synchronized Stream exec() throws OperationException { final Map conditions = Maps.newHashMap(); for (final URI component : this.conditions.keySet()) { conditions.put(component, merge(this.conditions.get(component))); } return doExec(this.timeout, conditions, this.ids, this.properties); } protected abstract Stream doExec(@Nullable final Long timeout, final Map conditions, final Map> ids, final Map> properties) throws OperationException; private URI checkComponent(final URI component) { final URI uri = COMPONENT_NORMALIZATION_MAP.get(component); Preconditions.checkArgument(uri != null, "Invalid match component %s", component); return uri; } } // TODO: add missing namespaces based on available namespaces /** * SPARQL query operation. *

* This operation evaluates a SPARQL query on the KnowledgeStore SPARQL endpoint. All SPARQL * query forms are supported: *

    *
  • SELECT queries return a {@code Stream} of {@code BindingSet}s (call * {@link Sparql#execTuples()});
  • *
  • CONSTRUCT and DESCRIBE queries return a {@code Stream} of {@link Statement}s (call * {@link Sparql#execTriples()});
  • *
  • ASK queries return a boolean result (call {@link Sparql#execBoolean()}).
  • *
*

*

* The operation can be configured, as usual, by specifying a timeout and supplying optional * namespace bindings to be used for parsing the SPARQL expression. In addition, * {@link Sparql#defaultGraphs(Iterable) default} and {@link Sparql#namedGraphs(Iterable) * named} graphs can be specified at the operation level, overriding the corresponding * declarations in the {@code FROM} and {@code FROM NAMED} clauses of the SPARQL query * expression. *

*/ public abstract static class Sparql extends Operation { private static final Pattern PLACEHOLDER_PATTERN = Pattern .compile("(?:(?<=\\A|[^\\\\]))([$][$])(?:(?=\\z|.))"); private final String expression; @Nullable private Set defaultGraphs; @Nullable private Set namedGraphs; /** * Creates a new {@code Sparql} operation instance (to be used in {@code Session} * implementations). * * @param namespaces * an immutable map of inherited namespaces, possibly null * @param expression * the SPARQL query expression, not null and possibly containing {@code $$} * placeholders * @param arguments * the values to assign to {@code $$} placeholders * @throws ParseException * in case the query expression is not valid */ protected Sparql(final Map namespaces, final String expression, final Object... arguments) throws ParseException { super(namespaces); this.expression = expand(expression, arguments); this.defaultGraphs = null; this.namedGraphs = null; } @Override public Sparql timeout(@Nullable final Long timeout) { return (Sparql) super.timeout(timeout); } @Override public Sparql namespaces(@Nullable final Map namespaces) { return (Sparql) super.namespaces(namespaces); } /** * Sets the optional default graphs for the query, overriding the default graphs specified * in the FROM clause. Passing null will remove any default graph previously set. * * @param defaultGraphs * a vararg array with the non-null URIs of the default graphs, possibly empty; * pass null to remove any default graph previously set * @return this operation object for call chaining */ public final synchronized Sparql defaultGraphs(@Nullable final URI... defaultGraphs) { return defaultGraphs(defaultGraphs == null ? null : Arrays.asList(defaultGraphs)); } /** * Sets the optional default graphs for the query, overriding default graphs specified in * the FROM clause. Passing null will remove any default graph previously set. * * @param defaultGraphs * an {@code Iterable} with the non-null URIs of the default graphs, possibly * empty; pass null to remove any default graph previously set * @return this operation object for call chaining */ public final synchronized Sparql defaultGraphs(// @Nullable final Iterable defaultGraphs) { this.defaultGraphs = defaultGraphs == null ? null : ImmutableSet.copyOf(defaultGraphs); return this; } /** * Sets the optional named graphs for the query, overriding named graphs specified in the * FROM NAMED clause. Passing null will remove any named graph previously set. * * @param namedGraphs * a vararg array with the non-null URIs of the named graphs, possibly empty; * pass null to remove any named graph previously set * @return this operation object for call chaining */ public final synchronized Sparql namedGraphs(@Nullable final URI... namedGraphs) { return namedGraphs(namedGraphs == null ? null : Arrays.asList(namedGraphs)); } /** * Sets the optional named graphs for the query, overriding named graphs specified in the * FROM NAMED clause. Passing null will remove any named graph previously set. * * @param namedGraphs * an {@code Iterable} with the non-null URIs of the named graphs, possibly * empty; pass null to remove any named graph previously set * @return this operation object for call chaining */ public final synchronized Sparql namedGraphs(@Nullable final Iterable namedGraphs) { this.namedGraphs = namedGraphs == null ? null : ImmutableSet.copyOf(namedGraphs); return this; } /** * Evaluates the query, returning its boolean result; applicable to ASK queries. * * @return the boolean result of the query * @throws OperationException * on failure (see possible outcome status codes) */ public final synchronized boolean execBoolean() throws OperationException { return doExec(this.timeout, Boolean.class, this.expression, this.defaultGraphs, this.namedGraphs).getUnique(); } /** * Evaluates the query, returning a {@code Stream} with the resulting {@code Statement}s; * applicable to CONSTRUCT and DESCRIBE queries. * * @return the resulting {@code Stream} of {@code Statement}s * @throws OperationException * on failure (see possible outcome status codes) */ public final synchronized Stream execTriples() throws OperationException { return doExec(this.timeout, Statement.class, this.expression, this.defaultGraphs, this.namedGraphs); } /** * Evaluates the query, returning a {@code Stream} with the resulting {@code BindingSet}s; * applicable to SELECT queries. After access to the returned {@code Stream}, a * {@code List} with output variables can be obtained by querying the * {@code Stream} metadata attribute {@code variables}. * * @return the resulting {@code Stream} of {@code BindingSet}s * @throws OperationException * on failure (see possible outcome status codes) */ public final synchronized Stream execTuples() throws OperationException { return doExec(this.timeout, BindingSet.class, this.expression, this.defaultGraphs, this.namedGraphs); } /** * Implementation method responsible of executing the SPARQL operation. * * @param timeout * the optional timeout for the operation; null if there is no timeout * @param type * the expected type of elements for the resulting {@code Stream}; either * {@link Statement}, {@link BindingSet} or {@link Boolean} * @param expression * the SPARQL query expression * @param defaultGraphs * the optional set of default graphs overriding the ones possibly specified in * the {@code FROM} query clause; if null, no override should take place * @param namedGraphs * the optional set of named graphs overriding the ones possibly specified in * the {@code FROM NAMED} query clause; if null, no override should take place * @param * the type of result elements * @return a {@code Stream} with the result of the query * @throws OperationException * in case of failure (see possible outcome status codes) */ protected abstract Stream doExec(@Nullable final Long timeout, final Class type, final String expression, @Nullable final Set defaultGraphs, @Nullable final Set namedGraphs) throws OperationException; private String expand(final String expression, final Object... arguments) throws ParseException { int expansions = 0; String result = expression; final Matcher matcher = PLACEHOLDER_PATTERN.matcher(expression); try { if (matcher.find()) { final StringBuilder builder = new StringBuilder(); int last = 0; do { Object arg = arguments[expansions++]; builder.append(expression.substring(last, matcher.start(1))); builder.append(arg instanceof Number ? arg : Data.toString(arg, null, false)); last = matcher.end(1); } while (matcher.find()); builder.append(expression.substring(last, expression.length())); result = builder.toString(); } } catch (final IndexOutOfBoundsException ex) { throw new ParseException(expression, "No argument supplied for placeholder #" + expansions); } if (expansions != arguments.length) { throw new ParseException(expression, "Expression string contains " + expansions + " placholders, but " + arguments.length + " arguments where supplied"); } return result; } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy