| /** |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.jena.sparql.core; |
| |
| import java.util.Iterator ; |
| |
| import org.apache.jena.atlas.iterator.Iter ; |
| import org.apache.jena.atlas.lib.Sync ; |
| import org.apache.jena.graph.*; |
| import org.apache.jena.graph.impl.GraphBase ; |
| import org.apache.jena.riot.other.GLib ; |
| import org.apache.jena.shared.AddDeniedException; |
| import org.apache.jena.shared.DeleteDeniedException; |
| import org.apache.jena.shared.JenaException ; |
| import org.apache.jena.shared.PrefixMapping ; |
| import org.apache.jena.shared.impl.PrefixMappingImpl ; |
| import org.apache.jena.sparql.SystemARQ ; |
| import org.apache.jena.sparql.graph.GraphUnionRead ; |
| import org.apache.jena.util.iterator.ExtendedIterator ; |
| import org.apache.jena.util.iterator.WrappedIterator ; |
| |
| /** Implement a Graph as a view of the DatasetGraph. |
| * |
| * It maps graph operations to quad operations. |
| * |
| * {@link GraphUnionRead} provides a union graph that does not assume quads, but loops on graphs. |
| * |
| * @see GraphUnionRead |
| */ |
| |
| public class GraphView extends GraphBase implements NamedGraph, Sync |
| { |
| // Beware this implements union graph - implementations may wish |
| // to do better so see protected method below. |
| |
| static class GraphViewException extends JenaException |
| { |
| public GraphViewException() { super(); } |
| public GraphViewException(String message) { super(message); } |
| public GraphViewException(Throwable cause) { super(cause) ; } |
| public GraphViewException(String message, Throwable cause) { super(message, cause) ; } |
| } |
| |
| private final DatasetGraph dsg ; |
| // null for default graph. |
| private final Node gn ; |
| private final TransactionHandlerView transactionHandler; |
| |
| // Factory style. |
| public static GraphView createDefaultGraph(DatasetGraph dsg) |
| { return new GraphView(dsg, Quad.defaultGraphNodeGenerated) ; } |
| |
| public static GraphView createNamedGraph(DatasetGraph dsg, Node graphIRI) |
| { return new GraphView(dsg, graphIRI) ; } |
| |
| public static GraphView createUnionGraph(DatasetGraph dsg) |
| { return new GraphView(dsg, Quad.unionGraph) ; } |
| |
| protected GraphView(DatasetGraph dsg, Node gn) { |
| this.dsg = dsg ; |
| this.gn = gn ; |
| this.transactionHandler = new TransactionHandlerView(dsg); |
| } |
| |
| /** |
| * Return the graph name for this graph in the dataset it is a view of. |
| * Returns {@code null} for the default graph. |
| */ |
| @Override |
| public Node getGraphName() { |
| return (gn == Quad.defaultGraphNodeGenerated) ? null : gn ; |
| } |
| |
| /** Return the {@link DatasetGraph} we are viewing. */ |
| public DatasetGraph getDataset() { |
| return dsg ; |
| } |
| |
| protected final boolean isDefaultGraph() { return isDefaultGraph(gn) ; } |
| protected final boolean isUnionGraph() { return isUnionGraph(gn) ; } |
| |
| protected static final boolean isDefaultGraph(Node gn) { return gn == null || Quad.isDefaultGraph(gn) ; } |
| protected static final boolean isUnionGraph(Node gn) { return Quad.isUnionGraph(gn) ; } |
| |
| @Override |
| protected PrefixMapping createPrefixMapping() { |
| // Subclasses should override this but in the absence of anything better ... |
| return new PrefixMappingImpl() ; |
| } |
| |
| @Override |
| protected ExtendedIterator<Triple> graphBaseFind(Triple m) { |
| if ( m == null ) m = Triple.ANY ; |
| Node s = m.getMatchSubject() ; |
| Node p = m.getMatchPredicate() ; |
| Node o = m.getMatchObject() ; |
| return graphBaseFind(s, p, o) ; |
| } |
| |
| @Override |
| protected ExtendedIterator<Triple> graphBaseFind(Node s, Node p, Node o) { |
| if ( Quad.isUnionGraph(gn) ) |
| return graphUnionFind(s, p, o) ; |
| Node g = graphNode(gn) ; |
| Iterator<Triple> iter = GLib.quads2triples(dsg.find(g, s, p, o)) ; |
| return WrappedIterator.createNoRemove(iter) ; |
| } |
| |
| private static Node graphNode(Node gn) { |
| return ( gn == null ) ? Quad.defaultGraphNodeGenerated : gn ; |
| } |
| |
| protected ExtendedIterator<Triple> graphUnionFind(Node s, Node p, Node o) { |
| Node g = graphNode(gn) ; |
| // Implementations may wish to do better so this is separated out. |
| // For example, Iter.distinctAdjacent is a lot cheaper than Iter.distinct |
| // but assumes things come back in a particular order |
| Iterator<Quad> iterQuads = getDataset().find(g, s, p, o) ; |
| Iterator<Triple> iter = GLib.quads2triples(iterQuads) ; |
| // Suppress duplicates after projecting to triples. |
| iter = Iter.distinct(iter) ; |
| return WrappedIterator.createNoRemove(iter) ; |
| } |
| |
| @Override |
| public void performAdd( Triple t ) { |
| Node g = graphNode(gn) ; |
| if ( Quad.isUnionGraph(g) ) |
| throw new AddDeniedException("Can't update the union graph of a dataset") ; |
| Node s = t.getSubject() ; |
| Node p = t.getPredicate() ; |
| Node o = t.getObject() ; |
| dsg.add(g, s, p, o) ; |
| } |
| |
| @Override |
| public void performDelete( Triple t ) { |
| Node g = graphNode(gn) ; |
| if ( Quad.isUnionGraph(g) ) |
| throw new DeleteDeniedException("Can't update the union graph of a dataset") ; |
| Node s = t.getSubject() ; |
| Node p = t.getPredicate() ; |
| Node o = t.getObject() ; |
| dsg.delete(g, s, p, o) ; |
| } |
| |
| @Override |
| public void remove(Node s, Node p, Node o) { |
| if ( getEventManager().listening() ) { |
| // Have to do it the hard way so that triple events happen. |
| super.remove(s, p, o); |
| return; |
| } |
| |
| dsg.deleteAny(getGraphName(), s, p, o); |
| // We know no one is listening ... |
| // getEventManager().notifyEvent(this, GraphEvents.remove(s, p, o) ); |
| } |
| |
| /** |
| * Subclasses may wish to provide {@code graphBaseSize} otherwise {@link GraphBase} uses {@code find()}. |
| */ |
| @Override protected int graphBaseSize() { return super.graphBaseSize(); } |
| |
| @Override |
| public void sync() { |
| SystemARQ.sync(dsg); |
| } |
| |
| @Override |
| public TransactionHandler getTransactionHandler() { |
| return new TransactionHandlerView(dsg); |
| } |
| |
| @Override |
| public Capabilities getCapabilities() { |
| if (capabilities == null) |
| capabilities = new GraphViewCapabilities(); |
| return capabilities; |
| } |
| |
| protected static class GraphViewCapabilities implements Capabilities { |
| @Override |
| public boolean sizeAccurate() { |
| return true; |
| } |
| |
| @Override |
| public boolean addAllowed() { |
| return addAllowed(false); |
| } |
| |
| @Override |
| public boolean addAllowed(boolean every) { |
| return true; |
| } |
| |
| @Override |
| public boolean deleteAllowed() { |
| return deleteAllowed(false); |
| } |
| |
| @Override |
| public boolean deleteAllowed(boolean every) { |
| return true; |
| } |
| |
| @Override |
| public boolean canBeEmpty() { |
| return true; |
| } |
| |
| @Override |
| public boolean iteratorRemoveAllowed() { |
| //Default for GraphViews is that iterators do not provide remove. |
| return false; |
| } |
| |
| @Override |
| public boolean findContractSafe() { |
| return true; |
| } |
| |
| @Override |
| public boolean handlesLiteralTyping() { |
| return false; |
| } |
| } |
| } |