/* * 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.rdf.model.impl; import java.util.* ; import java.util.function.Predicate; import org.apache.jena.graph.* ; import org.apache.jena.shared.AlreadyReifiedException ; import org.apache.jena.shared.CannotReifyException ; import org.apache.jena.util.iterator.ExtendedIterator ; import org.apache.jena.util.iterator.NullIterator ; import org.apache.jena.util.iterator.WrappedIterator ; import org.apache.jena.vocabulary.RDF ; /** A Reifier that only supports one style Standard (intercept, no conceal * -- and intercept is a no-op anyway because all triples * appear in the underlying graph for storing all triples). * This exists to give reification style "Standard" semantics primarly for legacy reasons. */ public class ReifierStd { private ReifierStd() {} private final static Node rdfType = RDF.Nodes.type ; private final static Node statement = RDF.Nodes.Statement ; private final static Node subject = RDF.Nodes.subject ; private final static Node predicate = RDF.Nodes.predicate ; private final static Node object = RDF.Nodes.object ; // All the methods of the old Reifier interface, converted to statics. /** Answer an iterator over the reification triples of this Reifier, or an empty iterator - if showHidden is false, only the exposed triples, otherwise only the concealed ones. */ public static ExtendedIterator<Triple> findEither(Graph graph, Triple match, boolean showHidden) { if ( showHidden ) return NullIterator.instance() ; else return graph.find(match) ; } static Predicate<Triple> filterReif = triple -> triple.getPredicate().equals(subject) || triple.getPredicate().equals(predicate) || triple.getPredicate().equals(object) || ( triple.getPredicate().equals(rdfType) && triple.getObject().equals(statement) ) ; /** Answer an iterator over all the reification triples that this Reifier exposes (ie all if Standard, none otherwise) that match m. */ public static ExtendedIterator<Triple> findExposed(Graph graph, Triple match) { ExtendedIterator<Triple> it = graph.find(match) ; it = it.filterKeep(filterReif) ; return WrappedIterator.create(it) ; } /** * Answer the triple associated with the node <code>n</code>. * * @param n * the node to use as the key * @return the associated triple, or <code>null</code> if none */ public static Triple getTriple(Graph graph, Node n) { // Must have rdf:type rdf:Statement if ( ! graph.contains(n, rdfType, statement) ) return null ; Node s = getObject(graph, n, subject) ; if ( s == null ) return null ; Node p = getObject(graph, n, predicate) ; if ( p == null ) return null ; Node o = getObject(graph, n, object) ; if ( o == null ) return null ; return new Triple(s,p,o) ; } // Get one and only one object private static Node getObject(Graph graph, Node n, Node predicate) { ExtendedIterator<Triple> iter = graph.find(n, predicate, Node.ANY) ; try { if ( ! iter.hasNext() ) // None. return null ; Triple t = iter.next() ; if ( iter.hasNext() ) // Too many. return null ; return t.getObject() ; } finally { iter.close() ; } } /** @return true iff there's > 0 mappings to this triple */ public static boolean hasTriple(Graph graph, Triple t) { ExtendedIterator<Node> iter = findNodesForTriple(graph, t, false) ; try { return iter.hasNext() ; } finally { iter.close() ; } } /** true iff _n_ is associated with some triple. */ public static boolean hasTriple(Graph graph, Node node) { return getTriple(graph, node) != null ; } /** * return an iterator over all the nodes that are reifiying something in the * graph */ public static ExtendedIterator<Node> allNodes(Graph graph) { return allNodes(graph, null) ; } /** * return an iterator over all the nodes that are reifiying t in the graph */ public static ExtendedIterator<Node> allNodes(Graph graph, Triple t) { return findNodesForTriple(graph, t, false) ; } private static ExtendedIterator<Node> findNodesForTriple(Graph graph, Triple t, boolean oneWillDo) { ExtendedIterator<Triple> iter = graph.find(Node.ANY, rdfType, statement) ; List<Node> nodes = new ArrayList<>() ; try { while (iter.hasNext()) { Triple typeTriple = iter.next() ; Node n = typeTriple.getSubject() ; // Check. if ( t != null ) { if ( ! exactlyOne(graph, n, subject, t.getSubject()) ) continue ; if ( ! exactlyOne(graph, n, predicate, t.getPredicate()) ) continue ; if ( ! exactlyOne(graph, n, object, t.getObject()) ) continue ; } nodes.add(n) ; if ( oneWillDo ) break ; } } finally { iter.close() ; } return WrappedIterator.createNoRemove(nodes.iterator()) ; } // ---- // check whether there is exactly the triple expected, and no others with same S and P but different O. private static boolean exactlyOne(Graph graph, Node n, Node predicate, Node object) { ExtendedIterator<Triple> iter = graph.find(n, predicate, Node.ANY) ; try { if ( ! iter.hasNext() ) return false ; while (iter.hasNext()) { Node obj = iter.next().getObject() ; if ( ! obj.equals(object) ) return false ; } return true ; } finally { iter.close() ; } } /** * note the triple _t_ as reified using _n_ as its representing node. If _n_ * is already reifying something, a AlreadyReifiedException is thrown. */ public static Node reifyAs(Graph graph, Node node, Triple triple) { if ( node == null ) node = NodeFactory.createBlankNode() ; else { Triple t = getTriple(graph, node) ; if ( t != null && ! t.equals(triple) ) throw new AlreadyReifiedException(node) ; if ( t != null ) // Already there return node ; // Check it's a well-formed reification by Jena's uniqueness rules // No fragments (we checked for exact match by getTriple(node)) if ( graph.contains(node, subject, Node.ANY) ) throw new CannotReifyException(node) ; if ( graph.contains(node, predicate, Node.ANY) ) throw new CannotReifyException(node) ; if ( graph.contains(node, object, Node.ANY) ) throw new CannotReifyException(node) ; } graph.add(new Triple(node, rdfType, statement)) ; graph.add(new Triple(node, subject, triple.getSubject())) ; graph.add(new Triple(node, predicate, triple.getPredicate())) ; graph.add(new Triple(node, object, triple.getObject())) ; return node ; } /** remove all bindings which map to this triple. */ public static void remove(Graph graph, Triple triple) { // Materialize the nodes to delete - avoid ConcurrentModificationException. for ( Node n : allNodes(graph, triple).toList() ) remove(graph, n, triple) ; } /** * remove any existing binding for _n_; hasNode(n) will return false and * getTriple(n) will return null. This only removes *unique, single* * bindings. */ public static void remove(Graph graph, Node node, Triple triple) { Set<Triple> triples = new HashSet<>(); triplesToZap(graph, triples, node, rdfType, statement) ; triplesToZap(graph, triples, node, subject, triple.getSubject()) ; triplesToZap(graph, triples, node, predicate, triple.getPredicate()) ; triplesToZap(graph, triples, node, object, triple.getObject()) ; for ( Triple t : triples ) graph.delete(t) ; } private static void triplesToZap(Graph graph, Collection<Triple> acc, Node s, Node p , Node o) { ExtendedIterator<Triple> iter = graph.find(s,p,o) ; while(iter.hasNext()) acc.add(iter.next()) ; } }