001/**
002 * Licensed to the Apache Software Foundation (ASF) under one
003 * or more contributor license agreements. See the NOTICE file
004 * distributed with this work for additional information
005 * regarding copyright ownership. The ASF licenses this file
006 * to you under the Apache License, Version 2.0 (the
007 * "License"); you may not use this file except in compliance
008 * 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.apache.commons.rdf.simple;
019
020import java.util.Optional;
021import java.util.stream.Stream;
022
023import org.apache.commons.rdf.api.BlankNode;
024import org.apache.commons.rdf.api.BlankNodeOrIRI;
025import org.apache.commons.rdf.api.Dataset;
026import org.apache.commons.rdf.api.Graph;
027import org.apache.commons.rdf.api.IRI;
028import org.apache.commons.rdf.api.Quad;
029import org.apache.commons.rdf.api.RDFTerm;
030import org.apache.commons.rdf.api.Triple;
031
032/**
033 * A {@link Graph} view on a {@link Dataset}.
034 * <p>
035 * This view is backed by a {@link Dataset}, and can be constructed in two ways:
036 *
037 * <dl>
038 * <dt>{@link #DatasetGraphView(Dataset)}</dt>
039 * <dd>Expose a <em>union graph</em> view of the Dataset, where all the
040 * {@link Quad}s of the Dataset is represented as a {@link Triple}. Adding
041 * triples will add them to the <em>default graph</em>, while removing triples
042 * will remove from all graphs.</dd>
043 *
044 * <dt>{@link #DatasetGraphView(Dataset, BlankNodeOrIRI)}</dt>
045 * <dd>Expose a particular graph of the Dataset, either named by an {@link IRI},
046 * a {@link BlankNode}, or <code>null</code> for the <em>default
047 * graph</em>.</dd>
048 * </dl>
049 * <p>
050 * Changes in the Graph are reflected directly in the Dataset and vice versa.
051 * This class is thread-safe is the underlying Dataset is thread-safe.
052 */
053public class DatasetGraphView implements Graph {
054
055    private final boolean unionGraph;
056    private final BlankNodeOrIRI namedGraph;
057    private final Dataset dataset;
058
059    public DatasetGraphView(final Dataset dataset) {
060        this.dataset = dataset;
061        this.namedGraph = null;
062        this.unionGraph = true;
063    }
064
065    public DatasetGraphView(final Dataset dataset, final BlankNodeOrIRI namedGraph) {
066        this.dataset = dataset;
067        this.namedGraph = namedGraph;
068        this.unionGraph = false;
069    }
070
071    @Override
072    public void close() throws Exception {
073        dataset.close();
074
075    }
076
077    @Override
078    public void add(final Triple triple) {
079        dataset.add(namedGraph, triple.getSubject(), triple.getPredicate(), triple.getObject());
080    }
081
082    @Override
083    public void add(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
084        dataset.add(namedGraph, subject, predicate, object);
085    }
086
087    @Override
088    public boolean contains(final Triple triple) {
089        return dataset.contains(unionOrNamedGraph(), triple.getSubject(), triple.getPredicate(), triple.getObject());
090    }
091
092    private Optional<BlankNodeOrIRI> unionOrNamedGraph() {
093        if (unionGraph) {
094            return null;
095        }
096        return Optional.ofNullable(namedGraph);
097    }
098
099    @Override
100    public boolean contains(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
101        return dataset.contains(unionOrNamedGraph(), subject, predicate, object);
102    }
103
104    @Override
105    public void remove(final Triple triple) {
106        dataset.remove(unionOrNamedGraph(), triple.getSubject(), triple.getPredicate(), triple.getObject());
107    }
108
109    @Override
110    public void remove(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
111        dataset.remove(unionOrNamedGraph(), subject, predicate, object);
112    }
113
114    @Override
115    public void clear() {
116        dataset.remove(unionOrNamedGraph(), null, null, null);
117    }
118
119    @Override
120    public long size() {
121        return stream().count();
122    }
123
124    @Override
125    public Stream<? extends Triple> stream() {
126        return stream(null, null, null);
127    }
128
129    @Override
130    public Stream<? extends Triple> stream(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
131        final Stream<Triple> stream = dataset.stream(unionOrNamedGraph(), subject, predicate, object).map(Quad::asTriple);
132        if (unionGraph) {
133            // remove duplicates
134            return stream.distinct();
135        }
136        return stream;
137    }
138
139}