/
DatasetImpl.java
212 lines (188 loc) · 8.04 KB
/
DatasetImpl.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
/**
* 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.commons.rdf.simple;
import java.util.HashSet;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.function.Predicate;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import org.apache.commons.rdf.api.BlankNode;
import org.apache.commons.rdf.api.BlankNodeOrIRI;
import org.apache.commons.rdf.api.Dataset;
import org.apache.commons.rdf.api.Graph;
import org.apache.commons.rdf.api.IRI;
import org.apache.commons.rdf.api.Literal;
import org.apache.commons.rdf.api.Quad;
import org.apache.commons.rdf.api.RDFTerm;
import org.apache.commons.rdf.simple.SimpleRDF.SimpleRDFTerm;
/**
* A simple, memory-based implementation of Dataset.
* <p>
* {@link Quad}s in the graph are kept in a {@link Set}.
* <p>
* All Stream operations are performed using parallel and unordered directives.
*/
final class DatasetImpl implements Dataset {
private static final int TO_STRING_MAX = 10;
private final Set<Quad> quads = new HashSet<>();
private final SimpleRDF factory;
DatasetImpl(final SimpleRDF simpleRDF) {
this.factory = simpleRDF;
}
@Override
public void add(final BlankNodeOrIRI graphName, final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
final BlankNodeOrIRI newGraphName = (BlankNodeOrIRI) internallyMap(graphName);
final BlankNodeOrIRI newSubject = (BlankNodeOrIRI) internallyMap(subject);
final IRI newPredicate = (IRI) internallyMap(predicate);
final RDFTerm newObject = internallyMap(object);
final Quad result = factory.createQuad(newGraphName, newSubject, newPredicate, newObject);
quads.add(result);
}
@Override
public void add(final Quad quad) {
final BlankNodeOrIRI newGraph = (BlankNodeOrIRI) internallyMap(quad.getGraphName().orElse(null));
final BlankNodeOrIRI newSubject = (BlankNodeOrIRI) internallyMap(quad.getSubject());
final IRI newPredicate = (IRI) internallyMap(quad.getPredicate());
final RDFTerm newObject = internallyMap(quad.getObject());
// Check if any of the object references changed during the mapping, to
// avoid creating a new Quad object if possible
if (newGraph == quad.getGraphName().orElse(null) && newSubject == quad.getSubject()
&& newPredicate == quad.getPredicate() && newObject == quad.getObject()) {
quads.add(quad);
} else {
// Make a new Quad with our mapped instances
final Quad result = factory.createQuad(newGraph, newSubject, newPredicate, newObject);
quads.add(result);
}
}
private <T extends RDFTerm> RDFTerm internallyMap(final T object) {
if (object == null || object instanceof SimpleRDFTerm) {
return object;
}
if (object instanceof BlankNode && !(object instanceof BlankNodeImpl)) {
final BlankNode blankNode = (BlankNode) object;
// This guarantees that adding the same BlankNode multiple times to
// this graph will generate a local object that is mapped to an
// equivalent object, based on the code in the package private
// BlankNodeImpl class
return factory.createBlankNode(blankNode.uniqueReference());
} else if (object instanceof IRI && !(object instanceof IRIImpl)) {
final IRI iri = (IRI) object;
return factory.createIRI(iri.getIRIString());
} else if (object instanceof Literal && !(object instanceof LiteralImpl)) {
final Literal literal = (Literal) object;
if (literal.getLanguageTag().isPresent()) {
return factory.createLiteral(literal.getLexicalForm(), literal.getLanguageTag().get());
}
return factory.createLiteral(literal.getLexicalForm(), (IRI) internallyMap(literal.getDatatype()));
} else {
throw new IllegalArgumentException("Not a BlankNode, IRI or Literal: " + object);
}
}
@Override
public void clear() {
quads.clear();
}
@Override
public boolean contains(final Optional<BlankNodeOrIRI> graphName, final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
return stream(graphName, subject, predicate, object).findAny().isPresent();
}
@Override
public boolean contains(final Quad quad) {
return quads.contains(Objects.requireNonNull(quad));
}
@Override
public Stream<Quad> stream() {
return quads.parallelStream().unordered();
}
@Override
public Stream<Quad> stream(final Optional<BlankNodeOrIRI> graphName, final BlankNodeOrIRI subject, final IRI predicate,
final RDFTerm object) {
final Optional<BlankNodeOrIRI> newGraphName;
if (graphName == null) {
// Avoid Optional<Optional<BlankNodeOrIRI>> ...
newGraphName = null;
} else {
newGraphName = graphName.map(g -> (BlankNodeOrIRI) internallyMap(g));
}
final BlankNodeOrIRI newSubject = (BlankNodeOrIRI) internallyMap(subject);
final IRI newPredicate = (IRI) internallyMap(predicate);
final RDFTerm newObject = internallyMap(object);
return getQuads(t -> {
if (newGraphName != null && !t.getGraphName().equals(newGraphName)) {
// This would check Optional.empty() == Optional.empty()
return false;
}
if (subject != null && !t.getSubject().equals(newSubject)) {
return false;
}
if (predicate != null && !t.getPredicate().equals(newPredicate)) {
return false;
}
if (object != null && !t.getObject().equals(newObject)) {
return false;
}
return true;
});
}
private Stream<Quad> getQuads(final Predicate<Quad> filter) {
return stream().filter(filter);
}
@Override
public void remove(final Optional<BlankNodeOrIRI> graphName, final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) {
final Stream<Quad> toRemove = stream(graphName, subject, predicate, object);
for (final Quad t : toRemove.collect(Collectors.toList())) {
// Avoid ConcurrentModificationException in ArrayList
remove(t);
}
}
@Override
public void remove(final Quad quad) {
quads.remove(Objects.requireNonNull(quad));
}
@Override
public long size() {
return quads.size();
}
@Override
public String toString() {
final String s = stream().limit(TO_STRING_MAX).map(Object::toString).collect(Collectors.joining("\n"));
if (size() > TO_STRING_MAX) {
return s + "\n# ... +" + (size() - TO_STRING_MAX) + " more";
}
return s;
}
@Override
public void close() {
}
@Override
public Graph getGraph() {
return getGraph(null).get();
}
@Override
public Optional<Graph> getGraph(final BlankNodeOrIRI graphName) {
return Optional.of(new DatasetGraphView(this, graphName));
}
@Override
public Stream<BlankNodeOrIRI> getGraphNames() {
// Not very efficient..
return stream().map(Quad::getGraphName).filter(Optional::isPresent).map(Optional::get).distinct();
}
}