/* * 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.graph.impl; import java.util.Collection ; import java.util.HashSet ; import java.util.Locale ; import java.util.Set ; import org.apache.jena.graph.Capabilities ; import org.apache.jena.graph.Node ; import org.apache.jena.graph.NodeFactory ; import org.apache.jena.graph.Triple ; import org.apache.jena.util.iterator.ExtendedIterator ; import org.apache.jena.util.iterator.WrappedIterator ; /** * A simple graph implementation that wraps a collection of triples. * * This is intended to be used in places where a graph is required but * iteration is the only expected operation. All graph operations are supported * but many are not efficient and will be slow on large collections. In these * cases a memory based graph will be more efficient. * <p> * This implementation: * <ul> * <li> * Does not support deleting triples from the iterator * </li><li> * Does not handle literal typing * </li></ul> */ public class CollectionGraph extends GraphBase { // override methods that need to be false off. private Capabilities cgCapabilities = new AllCapabilities() { @Override public boolean iteratorRemoveAllowed() { return iteratorDeleteAllowed; } @Override public boolean handlesLiteralTyping() { return false; } }; static boolean tripleContained(Triple patternTriple, Triple dataTriple) { return equalNode(patternTriple.getSubject(), dataTriple.getSubject()) && equalNode(patternTriple.getPredicate(), dataTriple.getPredicate()) && equalNode(patternTriple.getObject(), dataTriple.getObject()) ; } private static boolean equalNode(Node m, Node n) { // m should not be null unless .getMatchXXXX used to get the node. // Language tag canonicalization n = fixupNode(n) ; m = fixupNode(m) ; return (m==null) || (m == Node.ANY) || m.equals(n) ; } private static Node fixupNode(Node node) { if ( node == null || node == Node.ANY ) return node ; // RDF says ... language tags should be canonicalized to lower case. if ( node.isLiteral() ) { String lang = node.getLiteralLanguage() ; if ( lang != null && ! lang.equals("") ) node = NodeFactory.createLiteral(node.getLiteralLexicalForm(), lang.toLowerCase(Locale.ROOT)) ; } return node ; } // the collection private final Collection<Triple> triples; private final boolean uniqueOnly; private final boolean iteratorDeleteAllowed; /** * Construct an empty graph using an empty HashSet. * Iterator deletion is supported. */ public CollectionGraph() { this(new HashSet<>(), true); } /** * Construct a graph from a collection. * * Iterator deletion is not supported. * @param triples * The collection of triples. */ public CollectionGraph( final Collection<Triple> triples ) { this(triples, false); } /** * Construct a graph from a collection. * @param triples The collection of triples. * @param iteratorDeleteAllowed if true iterator on triple supports deletion and we want to enable iterator deletion. */ public CollectionGraph( final Collection<Triple> triples, boolean iteratorDeleteAllowed) { this.triples = triples; this.uniqueOnly = triples instanceof Set; this.iteratorDeleteAllowed = iteratorDeleteAllowed; } @Override protected ExtendedIterator<Triple> graphBaseFind( final Triple m ) { ExtendedIterator<Triple> iter =null; if (iteratorDeleteAllowed) { iter = SimpleEventManager.notifyingRemove( this, triples.iterator() ); } else { iter = WrappedIterator.createNoRemove( triples.iterator() ); } return iter.filterKeep ( t -> tripleContained(m, t) ); } @Override public void performAdd( final Triple t ) { if (uniqueOnly || !triples.contains(t)) { triples.add(t); } } @Override public void performDelete( final Triple t ) { triples.remove(t); } @Override public Capabilities getCapabilities() { return cgCapabilities; } }