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.

The 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 public abstract static class SparqlUpdate extends Operation { @Nullable private Stream statements; protected SparqlUpdate(final Map namespaces) throws ParseException { super(namespaces); } public final synchronized SparqlUpdate statements(@Nullable final Statement... statements) { this.statements = statements == null ? null : Stream.create(statements); return this; } public final synchronized SparqlUpdate statements(// @Nullable final Iterable statements) { this.statements = statements == null ? null : Stream.create(statements); return this; } public final synchronized Outcome exec() throws OperationException { return doExec(this.timeout, this.statements); } protected abstract Outcome doExec(@Nullable Long timeout, @Nullable final Stream statements) throws OperationException; } public abstract static class SparqlDelete extends Operation { @Nullable private Stream statements; protected SparqlDelete(final Map namespaces) throws ParseException { super(namespaces); } public final synchronized SparqlDelete statements(@Nullable final Statement... statements) { this.statements = statements == null ? null : Stream.create(statements); return this; } public final synchronized SparqlDelete statements(// @Nullable final Iterable statements) { this.statements = statements == null ? null : Stream.create(statements); return this; } public final synchronized Outcome exec() throws OperationException { return doExec(this.timeout, this.statements); } protected abstract Outcome doExec(@Nullable Long timeout, @Nullable final Stream statements) throws OperationException; } /** * 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 - 2025 Weber Informatics LLC | Privacy Policy