001/*
002 * Licensed to DuraSpace under one or more contributor license agreements.
003 * See the NOTICE file distributed with this work for additional information
004 * regarding copyright ownership.
005 *
006 * DuraSpace licenses this file to you under the Apache License,
007 * Version 2.0 (the "License"); you may not use this file except in
008 * compliance with the License.  You may obtain a copy of the License at
009 *
010 *     http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018package org.fcrepo.kernel.api.utils;
019
020import java.util.stream.Stream;
021
022import org.apache.jena.graph.Graph;
023import org.apache.jena.graph.Triple;
024import org.apache.jena.rdf.model.Model;
025
026import static java.util.Spliterator.IMMUTABLE;
027import static java.util.Spliterators.spliteratorUnknownSize;
028import static java.util.stream.StreamSupport.stream;
029import static org.apache.jena.graph.Node.ANY;
030import static org.apache.jena.sparql.graph.GraphFactory.createDefaultGraph;
031
032/**
033 * A wrapping {@link Stream} that calculates two differences between a
034 * {@link Graph} A and a source Stream B. The differences are (A - (A ∩ B)) and
035 * (B - (A ∩ B)). The ordinary output of this stream is (B - (A ∩ B)), and
036 * after exhaustion, sets containing (A - (A ∩ B)) and (A ∩ B) are available.
037 *
038 * @author ajs6f
039 * @author acoburn
040 * @since Oct 24, 2013
041 */
042public class GraphDifferencer {
043
044    private Graph notCommon;
045
046    private Graph common;
047
048    private Stream.Builder<Triple> source = Stream.builder();
049
050    /**
051     * Diff a Model against a stream of triples
052     *
053     * @param replacement the replacement
054     * @param original the original
055     */
056    public GraphDifferencer(final Model replacement,
057                                     final Stream<Triple> original) {
058        this(replacement.getGraph(), original);
059    }
060
061    /**
062     * Diff a graph against a stream of triples
063     *
064     * @param replacement the replacement
065     * @param original the original
066     */
067    public GraphDifferencer(final Graph replacement,
068                                     final Stream<Triple> original) {
069        notCommon = replacement;
070        common = createDefaultGraph();
071        original.forEach(x -> {
072            synchronized (this) {
073                if (notCommon.contains(x)) {
074                    notCommon.remove(x.getSubject(), x.getPredicate(), x.getObject());
075                    common.add(x);
076                } else if (!common.contains(x)) {
077                    source.accept(x);
078                }
079            }
080        });
081    }
082
083    /**
084     * This method returns the difference between the two input sources.
085     *
086     * @return The differences between the two inputs.
087     */
088    public Stream<Triple> difference() {
089        return source.build();
090    }
091
092    /**
093     * This method will return null until the source iterator is exhausted.
094     *
095     * @return The elements that turned out to be common to the two inputs.
096     */
097    public Stream<Triple> common() {
098        return stream(spliteratorUnknownSize(common.find(ANY, ANY, ANY), IMMUTABLE), false);
099    }
100
101    /**
102     * This method will return null until the source iterator is exhausted.
103     *
104     * @return The elements that turned out not to be common to the two inputs.
105     */
106    public Stream<Triple> notCommon() {
107        return stream(spliteratorUnknownSize(notCommon.find(ANY, ANY, ANY), IMMUTABLE), false);
108    }
109}