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.api.utils;
017
018import java.util.stream.Stream;
019
020import com.hp.hpl.jena.graph.Graph;
021import com.hp.hpl.jena.graph.Triple;
022import com.hp.hpl.jena.rdf.model.Model;
023
024import static java.util.Spliterator.IMMUTABLE;
025import static java.util.Spliterators.spliteratorUnknownSize;
026import static java.util.stream.StreamSupport.stream;
027import static com.hp.hpl.jena.graph.Node.ANY;
028import static com.hp.hpl.jena.sparql.graph.GraphFactory.createDefaultGraph;
029
030/**
031 * A wrapping {@link Stream} that calculates two differences between a
032 * {@link Graph} A and a source Stream B. The differences are (A - (A ∩ B)) and
033 * (B - (A ∩ B)). The ordinary output of this stream is (B - (A ∩ B)), and
034 * after exhaustion, sets containing (A - (A ∩ B)) and (A ∩ B) are available.
035 *
036 * @author ajs6f
037 * @author acoburn
038 * @since Oct 24, 2013
039 */
040public class GraphDifferencer {
041
042    private Graph notCommon;
043
044    private Graph common;
045
046    private Stream.Builder<Triple> source = Stream.builder();
047
048    /**
049     * Diff a Model against a stream of triples
050     *
051     * @param replacement the replacement
052     * @param original the original
053     */
054    public GraphDifferencer(final Model replacement,
055                                     final Stream<Triple> original) {
056        this(replacement.getGraph(), original);
057    }
058
059    /**
060     * Diff a graph against a stream of triples
061     *
062     * @param replacement the replacement
063     * @param original the original
064     */
065    public GraphDifferencer(final Graph replacement,
066                                     final Stream<Triple> original) {
067        notCommon = replacement;
068        common = createDefaultGraph();
069        original.forEach(x -> {
070            if (notCommon.contains(x)) {
071                notCommon.remove(x.getSubject(), x.getPredicate(), x.getObject());
072                common.add(x);
073            } else if (!common.contains(x)) {
074                source.accept(x);
075            }
076        });
077    }
078
079    /**
080     * This method returns the difference between the two input sources.
081     *
082     * @return The differences between the two inputs.
083     */
084    public Stream<Triple> difference() {
085        return source.build();
086    }
087
088    /**
089     * This method will return null until the source iterator is exhausted.
090     *
091     * @return The elements that turned out to be common to the two inputs.
092     */
093    public Stream<Triple> common() {
094        return stream(spliteratorUnknownSize(common.find(ANY, ANY, ANY), IMMUTABLE), false);
095    }
096
097    /**
098     * This method will return null until the source iterator is exhausted.
099     *
100     * @return The elements that turned out not to be common to the two inputs.
101     */
102    public Stream<Triple> notCommon() {
103        return stream(spliteratorUnknownSize(notCommon.find(ANY, ANY, ANY), IMMUTABLE), false);
104    }
105}