001/**
002 * Copyright 2015 DuraSpace, Inc.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016package org.fcrepo.kernel.utils.iterators;
017
018import java.util.Iterator;
019
020import com.google.common.collect.AbstractIterator;
021import com.hp.hpl.jena.graph.Graph;
022import com.hp.hpl.jena.graph.Triple;
023import com.hp.hpl.jena.rdf.model.Model;
024
025import static com.hp.hpl.jena.graph.Node.ANY;
026import static com.hp.hpl.jena.sparql.graph.GraphFactory.createDefaultGraph;
027
028/**
029 * A wrapping {@link Iterator} that calculates two differences between a
030 * {@link Graph} A and a source Iterator B. The differences are (A - (A ∩ B)) and
031 * (B - (A ∩ B)). The ordinary output of this iterator is (B - (A ∩ B)), and
032 * after exhaustion, sets containing (A - (A ∩ B)) and (A ∩ B) are available.
033 *
034 * @author ajs6f
035 * @since Oct 24, 2013
036 */
037public class GraphDifferencingIterator extends AbstractIterator<Triple> {
038
039    private Graph notCommon;
040
041    private Graph common;
042
043    private Iterator<Triple> source;
044
045    /**
046     * Diff a Model against a stream of triples
047     *
048     * @param replacement the replacement
049     * @param original the original
050     */
051    public GraphDifferencingIterator(final Model replacement,
052                                     final Iterator<Triple> original) {
053        this(replacement.getGraph(), original);
054    }
055
056    /**
057     * Diff a graph against a stream of triples
058     *
059     * @param replacement the replacement
060     * @param original the original
061     */
062    public GraphDifferencingIterator(final Graph replacement,
063                                     final Iterator<Triple> original) {
064        super();
065        this.notCommon = replacement;
066        this.common = createDefaultGraph();
067        this.source = original;
068
069    }
070
071    @Override
072    protected Triple computeNext() {
073        if (source.hasNext()) {
074            Triple next = source.next();
075            // we only want to return this element if it is not common
076            // to the two inputs
077            while (common.contains(next) || notCommon.contains(next)) {
078                // it was common, so shift it to common
079                if (notCommon.contains(next)) {
080                    notCommon.remove(next.getSubject(), next.getPredicate(), next.getObject());
081                    common.add(next);
082                }
083                // move onto the next candidate
084                if (!source.hasNext()) {
085                    return endOfData();
086                }
087                next = source.next();
088            }
089            // it was not common so return it
090            return next;
091        }
092        return endOfData();
093    }
094
095    /**
096     * This method will return null until the source iterator is exhausted.
097     *
098     * @return The elements that turned out to be common to the two inputs.
099     */
100    public Iterator<Triple> common() {
101        return source.hasNext() ? null : common.find(ANY, ANY, ANY);
102    }
103
104    /**
105     * This method will return null until the source iterator is exhausted.
106     *
107     * @return The elements that turned out not to be common to the two inputs.
108     */
109    public Iterator<Triple> notCommon() {
110        return source.hasNext() ? null : notCommon.find(ANY, ANY, ANY);
111    }
112
113}