├── .gitignore
├── LICENSE
├── README.md
├── pom.xml
└── src
├── main
└── java
│ └── net
│ └── fortytwo
│ └── tpop
│ └── sail
│ ├── DataStore.java
│ ├── GraphIndex.java
│ ├── GraphSail.java
│ ├── GraphSailConnection.java
│ ├── GraphSailDataset.java
│ ├── GraphSailSink.java
│ ├── GraphSailStatement.java
│ ├── GraphSailStore.java
│ ├── IterUtils.java
│ ├── NamespaceStore.java
│ ├── Schema.java
│ └── tg
│ └── TinkerGraphIndex.java
└── test
├── java
└── net
│ └── fortytwo
│ └── tpop
│ └── sail
│ ├── GetAndRetrieveStatementsTest.java
│ ├── GraphSailTest.java
│ ├── GraphSailTestBase.java
│ ├── NamespaceStoreTest.java
│ ├── SailAdder.java
│ └── SailTest.java
└── resources
└── net
└── fortytwo
└── tpop
└── sail
└── graph-example-sail-test.trig
/.gitignore:
--------------------------------------------------------------------------------
1 | *~
2 |
3 | # Eclipse
4 | .classpath
5 | .project
6 | .settings
7 |
8 | # Intellij
9 | .idea
10 | *.iml
11 |
12 | # Maven
13 | target/
14 | pom.xml.tag
15 | pom.xml.releaseBackup
16 | pom.xml.versionsBackup
17 | pom.xml.next
18 | release.properties
19 | dependency-reduced-pom.xml
20 | buildNumber.properties
21 | .mvn/timing.properties
22 | # Avoid ignoring Maven wrapper jar file (.jar files are usually ignored)
23 | !/.mvn/wrapper/maven-wrapper.jar
24 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "{}"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright {yyyy} {name of copyright owner}
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # GraphSail
2 |
3 | RDF storage and inference layer (SAIL) for Apache TinkerPop 3
4 |
5 | ## Namespaces
6 |
7 | Namespace definitions (prefix - IRI pairs) are a convenience provided in GraphSail for compatibility with RDF4j.
8 | However, they are not stored "in the graph" like RDF statements (which are represented as vertices and edges).
9 | Whereas Blueprints GraphSail attached namespaces to a special reference vertex, GraphSail v3 stores them in-memory.
10 | Read-write access to namespaces is very fast, but the namespaces persist only for the liftime of a session.
11 | In addition, changes to namespaces are not bound to TinkerPop transactions, so they are not rolled back if a write operation fails.
12 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 |
6 | 4.0.0
7 | net.fortytwo.tpop
8 | graphsail
9 | 1.0-SNAPSHOT
10 | jar
11 | GraphSail for TinkerPop3
12 | RDF storage and inference layer (SAIL) for Apache TinkerPop 3
13 |
14 |
15 | 2.2.2
16 | 3.2.5
17 | 4.12
18 |
19 |
20 |
21 |
22 |
23 | org.apache.tinkerpop
24 | gremlin-core
25 | ${tinkerpop.version}
26 |
27 |
28 | org.apache.tinkerpop
29 | tinkergraph-gremlin
30 | ${tinkerpop.version}
31 |
32 |
33 | org.eclipse.rdf4j
34 | rdf4j-sail-base
35 | ${rdf4j.version}
36 |
37 |
38 | org.eclipse.rdf4j
39 | rdf4j-sail-memory
40 | ${rdf4j.version}
41 |
42 |
43 |
44 |
45 | org.eclipse.rdf4j
46 | rdf4j-rio-trig
47 | ${rdf4j.version}
48 | test
49 |
50 |
51 | junit
52 | junit
53 | ${junit.version}
54 | test
55 |
56 |
57 |
58 |
59 |
60 | src/main/java
61 | src/test/java
62 | target
63 | ${project.artifactId}-${project.version}
64 |
65 |
66 | src/main/resources
67 |
68 |
69 |
70 |
71 | src/test/resources
72 |
73 |
74 |
75 |
76 | maven-compiler-plugin
77 | 2.3.2
78 |
79 | 1.8
80 | 1.8
81 |
82 |
83 |
84 |
85 |
86 |
87 |
--------------------------------------------------------------------------------
/src/main/java/net/fortytwo/tpop/sail/DataStore.java:
--------------------------------------------------------------------------------
1 | package net.fortytwo.tpop.sail;
2 |
3 | import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource;
4 | import org.apache.tinkerpop.gremlin.structure.Direction;
5 | import org.apache.tinkerpop.gremlin.structure.Edge;
6 | import org.apache.tinkerpop.gremlin.structure.Graph;
7 | import org.apache.tinkerpop.gremlin.structure.Property;
8 | import org.apache.tinkerpop.gremlin.structure.T;
9 | import org.apache.tinkerpop.gremlin.structure.Vertex;
10 | import org.eclipse.rdf4j.common.iteration.CloseableIteration;
11 | import org.eclipse.rdf4j.model.BNode;
12 | import org.eclipse.rdf4j.model.IRI;
13 | import org.eclipse.rdf4j.model.Literal;
14 | import org.eclipse.rdf4j.model.Resource;
15 | import org.eclipse.rdf4j.model.Statement;
16 | import org.eclipse.rdf4j.model.Value;
17 | import org.eclipse.rdf4j.model.ValueFactory;
18 | import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
19 | import org.eclipse.rdf4j.model.vocabulary.RDF;
20 | import org.eclipse.rdf4j.sail.SailException;
21 |
22 | import java.util.Collections;
23 | import java.util.HashSet;
24 | import java.util.Iterator;
25 | import java.util.Set;
26 | import java.util.function.Function;
27 |
28 | /**
29 | * A context object which is shared between the Blueprints Sail and its connections.
30 | *
31 | * @author Joshua Shinavier (http://fortytwo.net)
32 | */
33 | class DataStore {
34 |
35 | private final boolean readOnly;
36 | private final Graph graph;
37 | private final GraphTraversalSource traversal;
38 | private final NamespaceStore namespaces = new NamespaceStore();
39 | private final ValueFactory valueFactory = SimpleValueFactory.getInstance();
40 |
41 | private final GraphIndex valueIndex;
42 |
43 | private boolean uniqueStatements;
44 |
45 | private final SailChangedHelper sailChangedHelper;
46 |
47 | DataStore(final Graph graph,
48 | final boolean readOnly,
49 | final Function indexFactory,
50 | final SailChangedHelper sailChangedHelper) {
51 | this.graph = graph;
52 | this.traversal = graph.traversal();
53 | this.readOnly = readOnly;
54 | this.sailChangedHelper = sailChangedHelper;
55 |
56 | valueIndex = indexFactory.apply(Schema.VertexProperties.VALUE);
57 | valueIndex.initialize();
58 | }
59 |
60 | public Graph getGraph() {
61 | return graph;
62 | }
63 |
64 | boolean isReadOnly() {
65 | return readOnly;
66 | }
67 |
68 | NamespaceStore getNamespaces() {
69 | return namespaces;
70 | }
71 |
72 | private CloseableIteration extends Statement, SailException> getSubjectStatements(final Resource subject) {
73 | // assuming all edges are statements
74 | Vertex vertex = getVertexByValue(subject);
75 | Iterator edges = null == vertex
76 | ? Collections.emptyIterator()
77 | : vertex.edges(Direction.OUT);
78 | return toStatements(edges);
79 | }
80 |
81 | private CloseableIteration extends Statement, SailException> getObjectStatements(final Value object) {
82 | // assuming all edges are statements
83 | Vertex vertex = getVertexByValue(object);
84 | Iterator edges = null == vertex
85 | ? Collections.emptyIterator()
86 | : vertex.edges(Direction.IN);
87 | return toStatements(edges);
88 | }
89 |
90 | CloseableIteration extends Statement, SailException> getAllStatements() {
91 | return toStatements(getAllStatementEdges());
92 | }
93 |
94 | private Vertex getVertexByValue(final Value value) {
95 | return getVertexByValue(value, findLabel(value));
96 | }
97 |
98 | private Vertex getVertexByValue(final Value value, final Schema.VertexLabel vertexLabel) {
99 | Iterator hits = traversal.V()
100 | .has(T.label, vertexLabel.name()).has(Schema.VertexProperties.VALUE, value.stringValue());
101 | while (hits.hasNext()) {
102 | Vertex next = hits.next();
103 |
104 | // literals, additionally, may differ in datatype or language
105 | if (vertexLabel.equals(Schema.VertexLabel.Literal)
106 | && !datatypeAndLanguageEquals((Literal) value, next)) {
107 | continue;
108 | }
109 |
110 | return next;
111 | }
112 |
113 | return null;
114 | }
115 |
116 | boolean edgeExists(final Vertex outV, final Vertex inV, final String label, final String context) {
117 | Iterator edges = outV.edges(Direction.OUT, label);
118 | while (edges.hasNext()) {
119 | Edge next = edges.next();
120 | if (!next.inVertex().equals(inV)) continue;
121 | if (contextEquals(context, next)) {
122 | return true;
123 | }
124 | }
125 | return false;
126 | }
127 |
128 | Statement addStatementInternal(final Vertex outV, final Vertex inV, final String label, final String context) {
129 | Edge edge = outV.addEdge(label, inV);
130 | registerStatementAdded();
131 | if (null != context) {
132 | edge.property(Schema.EdgeProperties.CONTEXT, context);
133 | }
134 | return toStatement(edge);
135 | }
136 |
137 | private void registerStatementAdded() {
138 | sailChangedHelper.statementsAdded = true;
139 | }
140 |
141 | private void registerStatementRemoved() {
142 | sailChangedHelper.statementsRemoved = true;
143 | }
144 |
145 | private boolean contextEquals(final String expected, final Edge edge) {
146 | Property actual = edge.property(Schema.EdgeProperties.CONTEXT);
147 | if (actual.isPresent()) {
148 | return null != expected && actual.value().equals(expected);
149 | } else {
150 | return null == expected;
151 | }
152 | }
153 |
154 | private CloseableIteration extends Statement, SailException> toStatements(final Iterator edges) {
155 | return IterUtils.toCloseableIteration(edges, this::toStatement);
156 | }
157 |
158 | private boolean datatypeAndLanguageEquals(final Literal expected, final Vertex vertex) {
159 | String datatype = getDatatype(vertex);
160 | if (null == datatype || !datatype.equals(expected.getDatatype().stringValue())) {
161 | return false;
162 | }
163 | if (datatype.equals(RDF.LANGSTRING.stringValue())) {
164 | String language = getLanguage(vertex);
165 | if (null == language || !language.equals(expected.getLanguage().get())) {
166 | return false;
167 | }
168 | }
169 |
170 | return true;
171 | }
172 |
173 | Vertex getOrCreateVertexByValue(final Value value) {
174 | return getOrCreateVertexByValue(value, findLabel(value));
175 | }
176 |
177 | private Vertex getOrCreateVertexByValue(final Value value, final Schema.VertexLabel vertexLabel) {
178 | Vertex vertex = getVertexByValue(value, vertexLabel);
179 | if (null == vertex) {
180 | vertex = createNewVertex(value, vertexLabel);
181 | }
182 | return vertex;
183 | }
184 |
185 | private Vertex createNewVertex(final Value value, final Schema.VertexLabel vertexLabel) {
186 | switch (vertexLabel) {
187 | case IRI:
188 | return createNewIRIVertex(value);
189 | case BNode:
190 | return createNewBNodeVertex(value);
191 | case Literal:
192 | return createNewLiteralVertex(value);
193 | default:
194 | throw new IllegalStateException();
195 | }
196 | }
197 |
198 | private Vertex createVertex(final String label, final Value value) {
199 | Vertex vertex = graph.addVertex(label);
200 | vertex.property(Schema.VertexProperties.VALUE, value.stringValue());
201 | valueIndex.add(vertex);
202 | return vertex;
203 | }
204 |
205 | private Vertex createNewIRIVertex(final Value value) {
206 | return createVertex(Schema.VertexLabel.IRI.name(), value);
207 | }
208 |
209 | private Vertex createNewBNodeVertex(final Value value) {
210 | return createVertex(Schema.VertexLabel.BNode.name(), value);
211 | }
212 |
213 | private Vertex createNewLiteralVertex(final Value value) {
214 | Vertex vertex = createVertex(Schema.VertexLabel.Literal.name(), value);
215 | IRI datatype = ((Literal) value).getDatatype();
216 | vertex.property(Schema.VertexProperties.DATATYPE, datatype.stringValue());
217 | if (datatype.equals(RDF.LANGSTRING)) {
218 | vertex.property(Schema.VertexProperties.LANGUAGE, ((Literal) value).getLanguage().get());
219 | }
220 | return vertex;
221 | }
222 |
223 | private Schema.VertexLabel findLabel(final Value value) {
224 | if (value instanceof IRI) {
225 | return Schema.VertexLabel.IRI;
226 | } else if (value instanceof BNode) {
227 | return Schema.VertexLabel.BNode;
228 | } else if (value instanceof Literal) {
229 | return Schema.VertexLabel.Literal;
230 | } else {
231 | throw new IllegalArgumentException();
232 | }
233 | }
234 |
235 | // note: for now, every edge in the graph is assumed to be a statement edge
236 | private Iterator getAllStatementEdges() {
237 | return graph.edges();
238 | }
239 |
240 | private void removeEdgeCleanly(final Edge edge) {
241 | Vertex outV = edge.outVertex();
242 | Vertex inV = edge.inVertex();
243 | edge.remove();
244 | removeIfIsolated(outV);
245 | removeIfIsolated(inV);
246 | }
247 |
248 | void removeIteratorStatements(final CloseableIteration extends Statement, SailException> statements) {
249 | while (statements.hasNext()) {
250 | Statement next = statements.next();
251 |
252 | // beware of ConcurrentModificationExceptions
253 | removeEdgeCleanly(((GraphSailStatement) next).getEdge());
254 | registerStatementRemoved();
255 | }
256 | }
257 |
258 | private void removeIfIsolated(final Vertex toTest) {
259 | if (isIsolated(toTest)) {
260 | deleteVertex(toTest);
261 | }
262 | }
263 |
264 | private void deleteVertex(final Vertex toDelete) {
265 | valueIndex.remove(toDelete);
266 | toDelete.remove();
267 | }
268 |
269 | private boolean isIsolated(final Vertex toTest) {
270 | return isEmpty(toTest.edges(Direction.BOTH));
271 | }
272 |
273 | private boolean isEmpty(final Iterator iter) {
274 | return !iter.hasNext();
275 | }
276 |
277 | ValueFactory getValueFactory() {
278 | return valueFactory;
279 | }
280 |
281 | boolean getUniqueStatements() {
282 | return uniqueStatements;
283 | }
284 |
285 | void setUniqueStatements(boolean uniqueStatements) {
286 | this.uniqueStatements = uniqueStatements;
287 | }
288 |
289 | private Statement toStatement(final Edge edge) {
290 | return new GraphSailStatement(edge,
291 | getSubject(edge), getPredicate(edge), getObject(edge), getContext(edge));
292 | }
293 |
294 | private Resource getSubject(final Edge edge) {
295 | return toResource(edge.outVertex());
296 | }
297 |
298 | private IRI getPredicate(final Edge edge) {
299 | return toIRI(edge.label());
300 | }
301 |
302 | private Value getObject(final Edge edge) {
303 | return toValue(edge.inVertex());
304 | }
305 |
306 | private Resource getContext(final Edge edge) {
307 | Property prop = edge.property(Schema.EdgeProperties.CONTEXT);
308 | return prop.isPresent() ? toResource(prop.value()) : null;
309 | }
310 |
311 | private Resource toResource(final Vertex vertex) {
312 | return (Resource) toValue(vertex);
313 | }
314 |
315 | private IRI toIRI(final String iriValue) {
316 | return valueFactory.createIRI(iriValue);
317 | }
318 |
319 | private BNode toBNode(final String bNodeValue) {
320 | return valueFactory.createBNode(bNodeValue.substring(2));
321 | }
322 |
323 | private Resource toResource(final String iriOrBNodeValue) {
324 | // note: an IRI beginning with "_:" is not allowed
325 | if (iriOrBNodeValue.startsWith("_:")) {
326 | return toBNode(iriOrBNodeValue);
327 | } else {
328 | return toIRI(iriOrBNodeValue);
329 | }
330 | }
331 |
332 | private Value toValue(final Vertex vertex) {
333 | Schema.VertexLabel kind = Schema.VertexLabel.valueOf(vertex.label());
334 | switch (kind) {
335 | case IRI:
336 | return toIRI(vertex);
337 | case BNode:
338 | return toBNode(vertex);
339 | case Literal:
340 | return toLiteral(vertex);
341 | default:
342 | throw new IllegalStateException();
343 | }
344 | }
345 |
346 | private IRI toIRI(final Vertex vertex) {
347 | return toIRI(getValue(vertex));
348 | }
349 |
350 | private BNode toBNode(final Vertex vertex) {
351 | return toBNode(getValue(vertex));
352 | }
353 |
354 | private Literal toLiteral(final Vertex vertex) {
355 | String value = getValue(vertex);
356 | String datatype = getDatatype(vertex);
357 | if (datatype.equals(RDF.LANGSTRING.stringValue())) {
358 | String language = getLanguage(vertex);
359 | return valueFactory.createLiteral(value, language);
360 | } else {
361 | IRI dt = valueFactory.createIRI(datatype);
362 | return valueFactory.createLiteral(value, dt);
363 | }
364 | }
365 |
366 | private String getValue(final Vertex vertex) {
367 | return vertex.value(Schema.VertexProperties.VALUE);
368 | }
369 |
370 | private String getDatatype(final Vertex vertex) {
371 | return vertex.value(Schema.VertexProperties.DATATYPE);
372 | }
373 |
374 | private String getLanguage(final Vertex vertex) {
375 | Property prop = vertex.property(Schema.VertexProperties.LANGUAGE);
376 | return prop.isPresent() ? prop.value() : null;
377 | }
378 |
379 | CloseableIteration extends Statement, SailException> buildIterator(final Resource subject,
380 | final IRI predicate,
381 | final Value object,
382 | final Resource... contexts) {
383 | if (null != subject) {
384 | return getStatementsBySubject(subject, predicate, object, contexts);
385 | } else if (null != object) {
386 | return getStatementsByObject(subject, predicate, object, contexts);
387 | } else {
388 | return getStatementsWithFullScan(subject, predicate, object, contexts);
389 | }
390 | }
391 |
392 | private CloseableIteration extends Statement, SailException> getStatementsBySubject(
393 | final Resource subject,
394 | final IRI predicate,
395 | final Value object,
396 | final Resource... contexts) {
397 |
398 | CloseableIteration extends Statement, SailException> iter = getSubjectStatements(subject);
399 | if (null != object) {
400 | iter = addObjectFilter(iter, object);
401 | }
402 | if (null != predicate) {
403 | iter = addPredicateFilter(iter, predicate);
404 | }
405 | if (contexts.length > 0) {
406 | iter = addContextFilter(iter, contexts);
407 | }
408 | return iter;
409 | }
410 |
411 | private CloseableIteration extends Statement, SailException> getStatementsByObject(
412 | final Resource subject,
413 | final IRI predicate,
414 | final Value object,
415 | final Resource... contexts) {
416 | CloseableIteration extends Statement, SailException> iter = getObjectStatements(object);
417 | if (null != subject) {
418 | iter = addSubjectFilter(iter, subject);
419 | }
420 | if (null != predicate) {
421 | iter = addPredicateFilter(iter, predicate);
422 | }
423 | if (contexts.length > 0) {
424 | iter = addContextFilter(iter, contexts);
425 | }
426 | return iter;
427 | }
428 |
429 | private CloseableIteration extends Statement, SailException> getStatementsWithFullScan(
430 | final Resource subject,
431 | final IRI predicate,
432 | final Value object,
433 | final Resource... contexts) {
434 | CloseableIteration extends Statement, SailException> iter = getAllStatements();
435 | if (null != subject) {
436 | iter = addSubjectFilter(iter, subject);
437 | }
438 | if (null != object) {
439 | iter = addObjectFilter(iter, object);
440 | }
441 | if (null != predicate) {
442 | iter = addPredicateFilter(iter, predicate);
443 | }
444 | if (contexts.length > 0) {
445 | iter = addContextFilter(iter, contexts);
446 | }
447 | return iter;
448 | }
449 |
450 | private CloseableIteration addSubjectFilter(
451 | final CloseableIteration base, final Resource equalSubject) {
452 | return IterUtils.filter(base, statement -> statement.getSubject().equals(equalSubject));
453 | }
454 |
455 | private CloseableIteration addObjectFilter(
456 | final CloseableIteration base, final Value equalObject) {
457 | return IterUtils.filter(base, statement -> statement.getObject().equals(equalObject));
458 | }
459 |
460 | private CloseableIteration addPredicateFilter(
461 | final CloseableIteration base, final IRI equalPredicate) {
462 | return IterUtils.filter(base, statement -> statement.getPredicate().equals(equalPredicate));
463 | }
464 |
465 | private CloseableIteration addContextFilter(
466 | final CloseableIteration base, final Resource[] contexts) {
467 | Set contextSet = createNullSafeSetOfContexts(contexts);
468 | return IterUtils.filter(base,
469 | statement -> contextSet.contains(statement.getContext()));
470 | }
471 |
472 | static Set findContextsIn(final CloseableIteration extends Statement, SailException> iter) {
473 | Set contexts = new HashSet<>(); // may contain null
474 | while (iter.hasNext()) {
475 | contexts.add(iter.next().getContext());
476 | }
477 | return contexts;
478 | }
479 |
480 | Set createNullSafeSetOfContexts(final Resource[] contexts) {
481 | // HashSet explicitly allows null as an element
482 | Set set = new HashSet<>();
483 | Collections.addAll(set, contexts);
484 | return set;
485 | }
486 |
487 | SailChangedHelper getSailChangedHelper() {
488 | return sailChangedHelper;
489 | }
490 |
491 | abstract static class SailChangedHelper {
492 | private boolean statementsAdded;
493 | private boolean statementsRemoved;
494 |
495 | public abstract void notifyOfChanges(boolean statementsAdded, boolean statementsRemoved);
496 |
497 | synchronized void flush() {
498 | boolean addedTmp = statementsAdded;
499 | boolean removedTmp = statementsRemoved;
500 | statementsAdded = false;
501 | statementsRemoved = false;
502 | notifyOfChanges(addedTmp, removedTmp);
503 | }
504 | }
505 | }
506 |
--------------------------------------------------------------------------------
/src/main/java/net/fortytwo/tpop/sail/GraphIndex.java:
--------------------------------------------------------------------------------
1 | package net.fortytwo.tpop.sail;
2 |
3 | import org.apache.tinkerpop.gremlin.structure.Vertex;
4 |
5 | public abstract class GraphIndex {
6 | protected final String key;
7 |
8 | protected GraphIndex(final String key) {
9 | this.key = key;
10 | }
11 |
12 | protected abstract boolean isAutomatic();
13 |
14 | public abstract void initialize();
15 |
16 | protected abstract void addInternal(Vertex vertex);
17 |
18 | public abstract void removeInternal(Vertex vertex);
19 |
20 | void add(Vertex vertex) {
21 | if (!isAutomatic()) {
22 | addInternal(vertex);
23 | }
24 | }
25 |
26 | void remove(Vertex vertex) {
27 | if (!isAutomatic()) {
28 | removeInternal(vertex);
29 | }
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/src/main/java/net/fortytwo/tpop/sail/GraphSail.java:
--------------------------------------------------------------------------------
1 | package net.fortytwo.tpop.sail;
2 |
3 | import org.apache.tinkerpop.gremlin.structure.Graph;
4 | import org.eclipse.rdf4j.model.ValueFactory;
5 | import org.eclipse.rdf4j.query.algebra.evaluation.EvaluationStrategyFactory;
6 | import org.eclipse.rdf4j.query.algebra.evaluation.federation.FederatedServiceResolver;
7 | import org.eclipse.rdf4j.query.algebra.evaluation.federation.FederatedServiceResolverClient;
8 | import org.eclipse.rdf4j.query.algebra.evaluation.federation.FederatedServiceResolverImpl;
9 | import org.eclipse.rdf4j.query.algebra.evaluation.impl.StrictEvaluationStrategyFactory;
10 | import org.eclipse.rdf4j.sail.NotifyingSailConnection;
11 | import org.eclipse.rdf4j.sail.Sail;
12 | import org.eclipse.rdf4j.sail.SailChangedEvent;
13 | import org.eclipse.rdf4j.sail.SailException;
14 | import org.eclipse.rdf4j.sail.base.SailStore;
15 | import org.eclipse.rdf4j.sail.helpers.AbstractNotifyingSail;
16 |
17 | import java.util.function.Function;
18 |
19 | /**
20 | * An RDF storage and inference layer (SAIL) for Apache TinkerPop 3
21 | *
22 | * @author Joshua Shinavier (http://fortytwo.net)
23 | */
24 | public class GraphSail extends AbstractNotifyingSail {
25 | private final DataStore dataStore;
26 | private final SailStore sailStore;
27 |
28 | private EvaluationStrategyFactory evalStratFactory;
29 | private FederatedServiceResolver serviceResolver;
30 | private FederatedServiceResolverImpl dependentServiceResolver;
31 |
32 | public GraphSail(final Graph graph, final Function indexFactory) {
33 | this(graph, indexFactory, false);
34 | }
35 |
36 | public GraphSail(final Graph graph, final Function indexFactory, final boolean readOnly) {
37 | this.dataStore = new DataStore(graph, readOnly, indexFactory, new DataStore.SailChangedHelper() {
38 | @Override
39 | public void notifyOfChanges(boolean statementsAdded, boolean statementsRemoved) {
40 | if (statementsAdded || statementsRemoved) {
41 | SailChangedEvent event = new SailChangedEvent() {
42 | @Override
43 | public Sail getSail() {
44 | return GraphSail.this;
45 | }
46 |
47 | @Override
48 | public boolean statementsAdded() {
49 | return statementsAdded;
50 | }
51 |
52 | @Override
53 | public boolean statementsRemoved() {
54 | return statementsRemoved;
55 | }
56 | };
57 | notifySailChanged(event);
58 | }
59 | }
60 | });
61 | this.sailStore = new GraphSailStore(dataStore);
62 | }
63 |
64 | DataStore getDataStore() {
65 | return dataStore;
66 | }
67 |
68 | SailStore getSailStore() {
69 | return sailStore;
70 | }
71 |
72 | synchronized EvaluationStrategyFactory getEvaluationStrategyFactory() {
73 | if (evalStratFactory == null) {
74 | evalStratFactory = new StrictEvaluationStrategyFactory(getFederatedServiceResolver());
75 | }
76 | evalStratFactory.setQuerySolutionCacheThreshold(getIterationCacheSyncThreshold());
77 | return evalStratFactory;
78 | }
79 |
80 | private synchronized FederatedServiceResolver getFederatedServiceResolver() {
81 | if (serviceResolver == null) {
82 | if (dependentServiceResolver == null) {
83 | dependentServiceResolver = new FederatedServiceResolverImpl();
84 | }
85 | setFederatedServiceResolver(dependentServiceResolver);
86 | }
87 | return serviceResolver;
88 | }
89 |
90 | private synchronized void setFederatedServiceResolver(FederatedServiceResolver resolver) {
91 | this.serviceResolver = resolver;
92 | if (resolver != null && evalStratFactory instanceof FederatedServiceResolverClient) {
93 | ((FederatedServiceResolverClient) evalStratFactory).setFederatedServiceResolver(resolver);
94 | }
95 | }
96 |
97 | /**
98 | * Enables or disables enforcement of a unique statements policy (disabled by default),
99 | * which ensures that no new statement will be added which is identical
100 | * (in all of its subject, predicate, object and context) to an existing statement.
101 | * If enabled, this policy will first remove any existing statements identical to the to-be-added statement,
102 | * before adding the latter statement.
103 | * This comes at the cost of higher write latency.
104 | *
105 | * @param flag whether this policy should be enforced
106 | */
107 | public void enforceUniqueStatements(final boolean flag) {
108 | dataStore.setUniqueStatements(flag);
109 | }
110 |
111 | @Override
112 | protected void shutDownInternal() throws SailException {
113 | wrapForSail(sailStore::close);
114 | }
115 |
116 | @Override
117 | protected NotifyingSailConnection getConnectionInternal() throws SailException {
118 | return new GraphSailConnection(this);
119 | }
120 |
121 | @Override
122 | public boolean isWritable() throws SailException {
123 | return !dataStore.isReadOnly();
124 | }
125 |
126 | @Override
127 | public ValueFactory getValueFactory() {
128 | return dataStore.getValueFactory();
129 | }
130 |
131 | private void wrapForSail(final NoargMethod method) {
132 | try {
133 | method.call();
134 | } catch (Exception e) {
135 | throw new SailException(e);
136 | }
137 | }
138 |
139 | @FunctionalInterface
140 | private interface NoargMethod {
141 | void call() throws Exception;
142 | }
143 | }
--------------------------------------------------------------------------------
/src/main/java/net/fortytwo/tpop/sail/GraphSailConnection.java:
--------------------------------------------------------------------------------
1 | package net.fortytwo.tpop.sail;
2 |
3 | import org.apache.tinkerpop.gremlin.structure.Vertex;
4 | import org.eclipse.rdf4j.model.IRI;
5 | import org.eclipse.rdf4j.model.Resource;
6 | import org.eclipse.rdf4j.model.Value;
7 | import org.eclipse.rdf4j.sail.SailException;
8 | import org.eclipse.rdf4j.sail.base.SailSourceConnection;
9 |
10 | /**
11 | * A transactional connection to a GraphSail RDF store
12 | *
13 | * @author Joshua Shinavier (http://fortytwo.net)
14 | */
15 | class GraphSailConnection extends SailSourceConnection {
16 |
17 | private static final Resource[] DEFAULT_CONTEXT = {null};
18 |
19 | private final DataStore dataStore;
20 |
21 | GraphSailConnection(GraphSail sail) {
22 | super(sail, sail.getSailStore(), sail.getEvaluationStrategyFactory());
23 | this.dataStore = sail.getDataStore();
24 | }
25 |
26 | @Override
27 | public void addStatementInternal(
28 | final Resource subject,
29 | final IRI predicate,
30 | final Value object,
31 | final Resource... contexts) throws SailException {
32 | Vertex subjectVertex = dataStore.getOrCreateVertexByValue(subject);
33 | Vertex objectVertex = dataStore.getOrCreateVertexByValue(object);
34 | Resource[] addContexts = 0 == contexts.length
35 | ? DEFAULT_CONTEXT
36 | : contexts;
37 | String label = predicate.stringValue();
38 | for (Resource context : addContexts) {
39 | String contextValue = null == context ? null : context.stringValue();
40 | if (dataStore.getUniqueStatements()
41 | && dataStore.edgeExists(subjectVertex, objectVertex, label, contextValue)) {
42 | continue;
43 | }
44 |
45 | dataStore.addStatementInternal(subjectVertex, objectVertex, label, contextValue);
46 | }
47 | }
48 |
49 | @Override
50 | public void removeStatementsInternal(final Resource subject,
51 | final IRI predicate,
52 | final Value object,
53 | final Resource... contexts) throws SailException {
54 | dataStore.removeIteratorStatements(dataStore.buildIterator(subject, predicate, object, contexts));
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/src/main/java/net/fortytwo/tpop/sail/GraphSailDataset.java:
--------------------------------------------------------------------------------
1 | package net.fortytwo.tpop.sail;
2 |
3 | import org.eclipse.rdf4j.common.iteration.CloseableIteration;
4 | import org.eclipse.rdf4j.model.IRI;
5 | import org.eclipse.rdf4j.model.Namespace;
6 | import org.eclipse.rdf4j.model.Resource;
7 | import org.eclipse.rdf4j.model.Statement;
8 | import org.eclipse.rdf4j.model.Value;
9 | import org.eclipse.rdf4j.sail.SailException;
10 | import org.eclipse.rdf4j.sail.base.SailDataset;
11 |
12 | import java.util.Set;
13 |
14 | class GraphSailDataset implements SailDataset {
15 | private final DataStore dataStore;
16 |
17 | GraphSailDataset(DataStore dataStore) {
18 | this.dataStore = dataStore;
19 | }
20 |
21 | @Override
22 | public void close() throws SailException {
23 | // TODO: graph transaction handling
24 | }
25 |
26 | @Override
27 | public CloseableIteration extends Namespace, SailException> getNamespaces() throws SailException {
28 | return dataStore.getNamespaces().getAll();
29 | }
30 |
31 | @Override
32 | public String getNamespace(final String prefix) throws SailException {
33 | return dataStore.getNamespaces().get(prefix);
34 | }
35 |
36 | @Override
37 | public CloseableIteration extends Resource, SailException> getContextIDs() throws SailException {
38 | Set contexts = DataStore.findContextsIn(dataStore.getAllStatements());
39 | return IterUtils.toCloseableIteration(contexts.iterator(), s -> s);
40 | }
41 |
42 | @Override
43 | public CloseableIteration extends Statement, SailException> getStatements(
44 | Resource subject, IRI predicate, Value object, Resource... contexts) throws SailException {
45 | return dataStore.buildIterator(subject, predicate, object, contexts);
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/net/fortytwo/tpop/sail/GraphSailSink.java:
--------------------------------------------------------------------------------
1 | package net.fortytwo.tpop.sail;
2 |
3 | import org.eclipse.rdf4j.common.iteration.CloseableIteration;
4 | import org.eclipse.rdf4j.model.IRI;
5 | import org.eclipse.rdf4j.model.Resource;
6 | import org.eclipse.rdf4j.model.Statement;
7 | import org.eclipse.rdf4j.model.Value;
8 | import org.eclipse.rdf4j.sail.SailException;
9 | import org.eclipse.rdf4j.sail.base.SailSink;
10 |
11 | import java.util.Set;
12 |
13 | class GraphSailSink implements SailSink {
14 | private final DataStore dataStore;
15 |
16 | GraphSailSink(final DataStore dataStore) {
17 | this.dataStore = dataStore;
18 | }
19 |
20 | @Override
21 | public void prepare() throws SailException {
22 | // TODO: graph transaction handling
23 | }
24 |
25 | @Override
26 | public void flush() throws SailException {
27 | dataStore.getSailChangedHelper().flush();
28 | // TODO: graph transaction handling
29 | }
30 |
31 | @Override
32 | public void setNamespace(final String prefix, final String name) throws SailException {
33 | dataStore.getNamespaces().set(prefix, name);
34 | }
35 |
36 | @Override
37 | public void removeNamespace(final String prefix) throws SailException {
38 | dataStore.getNamespaces().remove(prefix);
39 | }
40 |
41 | @Override
42 | public void clearNamespaces() throws SailException {
43 | dataStore.getNamespaces().clear();
44 | }
45 |
46 | @Override
47 | public void clear(Resource... contexts) throws SailException {
48 | Set contextSet = contexts.length == 0 ? null : dataStore.createNullSafeSetOfContexts(contexts);
49 |
50 | CloseableIteration extends Statement, SailException> edges = dataStore.getAllStatements();
51 | if (0 < contexts.length) {
52 | edges = IterUtils.filter(edges,
53 | st -> contextSet.contains(st.getContext()));
54 | }
55 |
56 | dataStore.removeIteratorStatements(edges);
57 | }
58 |
59 | @Override
60 | public void observe(Resource subject, IRI predicate, Value object, Resource... contexts) throws SailException {
61 | // TODO: no-op?
62 | }
63 |
64 | @Override
65 | public void approve(Resource subject, IRI predicate, Value object, Resource context) throws SailException {
66 | // TODO: no-op?
67 | }
68 |
69 | @Override
70 | public void deprecate(Resource subject, IRI predicate, Value object, Resource context) throws SailException {
71 | // TODO: no-op?
72 | }
73 |
74 | @Override
75 | public void close() throws SailException {
76 | // TODO: graph transaction handling
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/src/main/java/net/fortytwo/tpop/sail/GraphSailStatement.java:
--------------------------------------------------------------------------------
1 | package net.fortytwo.tpop.sail;
2 |
3 | import org.apache.tinkerpop.gremlin.structure.Edge;
4 | import org.eclipse.rdf4j.model.IRI;
5 | import org.eclipse.rdf4j.model.Resource;
6 | import org.eclipse.rdf4j.model.Value;
7 | import org.eclipse.rdf4j.model.impl.ContextStatement;
8 |
9 | class GraphSailStatement extends ContextStatement {
10 | private final Edge edge;
11 |
12 | GraphSailStatement(
13 | final Edge edge, final Resource subject, final IRI predicate, final Value object, final Resource context) {
14 | super(subject, predicate, object, context);
15 | this.edge = edge;
16 | }
17 |
18 | Edge getEdge() {
19 | return edge;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/src/main/java/net/fortytwo/tpop/sail/GraphSailStore.java:
--------------------------------------------------------------------------------
1 | package net.fortytwo.tpop.sail;
2 |
3 | import org.eclipse.rdf4j.IsolationLevel;
4 | import org.eclipse.rdf4j.model.ValueFactory;
5 | import org.eclipse.rdf4j.query.algebra.evaluation.impl.EvaluationStatistics;
6 | import org.eclipse.rdf4j.sail.SailException;
7 | import org.eclipse.rdf4j.sail.base.BackingSailSource;
8 | import org.eclipse.rdf4j.sail.base.SailDataset;
9 | import org.eclipse.rdf4j.sail.base.SailSink;
10 | import org.eclipse.rdf4j.sail.base.SailSource;
11 | import org.eclipse.rdf4j.sail.base.SailStore;
12 | import org.slf4j.Logger;
13 | import org.slf4j.LoggerFactory;
14 |
15 | class GraphSailStore implements SailStore {
16 | private final Logger logger = LoggerFactory.getLogger(GraphSailStore.class);
17 |
18 | private final DataStore dataStore;
19 | private final SailSource sailSource;
20 |
21 | GraphSailStore(DataStore dataStore) {
22 | this.dataStore = dataStore;
23 | sailSource = new BackingSailSource() {
24 | @Override
25 | public SailSink sink(IsolationLevel isolationLevel) throws SailException {
26 | return new GraphSailSink(dataStore);
27 | }
28 |
29 | @Override
30 | public SailDataset dataset(IsolationLevel isolationLevel) throws SailException {
31 | return new GraphSailDataset(dataStore);
32 | }
33 | };
34 | }
35 |
36 | @Override
37 | public ValueFactory getValueFactory() {
38 | return null;
39 | }
40 |
41 | @Override
42 | public EvaluationStatistics getEvaluationStatistics() {
43 | // TODO
44 | return new EvaluationStatistics();
45 | }
46 |
47 | @Override
48 | public SailSource getExplicitSailSource() {
49 | return sailSource;
50 | }
51 |
52 | @Override
53 | public SailSource getInferredSailSource() {
54 | return sailSource;
55 | }
56 |
57 | @Override
58 | public void close() {
59 | try {
60 | dataStore.getGraph().close();
61 | } catch (Exception e) {
62 | logger.error("failed to close graph", e);
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/src/main/java/net/fortytwo/tpop/sail/IterUtils.java:
--------------------------------------------------------------------------------
1 | package net.fortytwo.tpop.sail;
2 |
3 | import org.eclipse.rdf4j.common.iteration.CloseableIteration;
4 |
5 | import java.util.Collection;
6 | import java.util.Iterator;
7 | import java.util.LinkedList;
8 | import java.util.NoSuchElementException;
9 | import java.util.function.Function;
10 | import java.util.function.Predicate;
11 |
12 | class IterUtils {
13 | static CloseableIteration toCloseableIteration(
14 | final Iterator iterator,
15 | final Function mapping) {
16 | return new CloseableIteration() {
17 | @Override
18 | public void close() throws X {
19 | // do nothing
20 | }
21 |
22 | @Override
23 | public boolean hasNext() throws X {
24 | return iterator.hasNext();
25 | }
26 |
27 | @Override
28 | public R next() throws X {
29 | return mapping.apply(iterator.next());
30 | }
31 |
32 | @Override
33 | public void remove() throws X {
34 | throw new UnsupportedOperationException();
35 | }
36 | };
37 | }
38 |
39 | static CloseableIteration filter(
40 | final CloseableIteration base,
41 | final Predicate criterion) {
42 | return new CloseableIteration() {
43 | private T nextElement;
44 |
45 | @Override
46 | public void close() throws X {
47 | base.close();
48 | }
49 |
50 | @Override
51 | public boolean hasNext() throws X {
52 | lookAhead();
53 | return null != nextElement;
54 | }
55 |
56 | @Override
57 | public T next() throws X {
58 | lookAhead();
59 | if (null == nextElement) {
60 | throw new NoSuchElementException();
61 | }
62 |
63 | T tmp = nextElement;
64 | nextElement = null;
65 | return tmp;
66 | }
67 |
68 | @Override
69 | public void remove() throws X {
70 | throw new UnsupportedOperationException();
71 | }
72 |
73 | private void lookAhead() throws X {
74 | while (null == nextElement) {
75 | if (!base.hasNext()) break;
76 |
77 | T tmp = base.next();
78 | if (criterion.test(tmp)) {
79 | nextElement = tmp;
80 | }
81 | }
82 | }
83 | };
84 | }
85 |
86 | static Collection collect(final CloseableIteration iter) throws X {
87 | try {
88 | Collection result = new LinkedList<>();
89 | while (iter.hasNext()) {
90 | result.add(iter.next());
91 | }
92 | return result;
93 | } finally {
94 | iter.close();
95 | }
96 | }
97 |
98 | static long count(final CloseableIteration iter) throws X {
99 | try {
100 | long count = 0;
101 | while (iter.hasNext()) {
102 | iter.next();
103 | count++;
104 | }
105 | return count;
106 | } finally {
107 | iter.close();
108 | }
109 | }
110 | }
111 |
--------------------------------------------------------------------------------
/src/main/java/net/fortytwo/tpop/sail/NamespaceStore.java:
--------------------------------------------------------------------------------
1 | package net.fortytwo.tpop.sail;
2 |
3 | import com.google.common.base.Preconditions;
4 | import org.eclipse.rdf4j.common.iteration.CloseableIteration;
5 | import org.eclipse.rdf4j.model.Namespace;
6 | import org.eclipse.rdf4j.model.impl.SimpleNamespace;
7 | import org.eclipse.rdf4j.sail.SailException;
8 |
9 | import java.util.Map;
10 | import java.util.concurrent.ConcurrentHashMap;
11 |
12 | /**
13 | * An in-memory store for IRI namespace definitions.
14 | * This store is not persisted between sessions.
15 | *
16 | * @author Joshua Shinavier (http://fortytwo.net)
17 | */
18 | class NamespaceStore {
19 | private final Map namesByPrefix = new ConcurrentHashMap<>();
20 |
21 | String get(final String prefix) {
22 | Preconditions.checkNotNull(prefix);
23 |
24 | return namesByPrefix.get(trim(prefix));
25 | }
26 |
27 | void set(final String prefix, final String name) {
28 | Preconditions.checkNotNull(prefix);
29 | Preconditions.checkNotNull(name);
30 |
31 | namesByPrefix.put(trim(prefix), trim(name));
32 | }
33 |
34 | void remove(final String prefix) {
35 | Preconditions.checkNotNull(trim(prefix));
36 |
37 | namesByPrefix.remove(trim(prefix));
38 | }
39 |
40 | void clear() {
41 | namesByPrefix.clear();
42 | }
43 |
44 | CloseableIteration extends Namespace, SailException> getAll() {
45 | return IterUtils.toCloseableIteration(namesByPrefix.entrySet().iterator(),
46 | e -> new SimpleNamespace(e.getKey(), e.getValue()));
47 | }
48 |
49 | private String trim(final String untrimmed) {
50 | return untrimmed.trim();
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/src/main/java/net/fortytwo/tpop/sail/Schema.java:
--------------------------------------------------------------------------------
1 | package net.fortytwo.tpop.sail;
2 |
3 | interface Schema {
4 | enum VertexLabel {IRI, BNode, Literal}
5 |
6 | interface VertexProperties {
7 | String DATATYPE = "datatype";
8 | String LANGUAGE = "language";
9 | String VALUE = "value";
10 | }
11 |
12 | interface EdgeProperties {
13 | String CONTEXT = "context";
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/main/java/net/fortytwo/tpop/sail/tg/TinkerGraphIndex.java:
--------------------------------------------------------------------------------
1 | package net.fortytwo.tpop.sail.tg;
2 |
3 | import com.google.common.base.Preconditions;
4 | import net.fortytwo.tpop.sail.GraphIndex;
5 | import org.apache.tinkerpop.gremlin.structure.Vertex;
6 | import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph;
7 |
8 | public class TinkerGraphIndex extends GraphIndex {
9 | private final TinkerGraph graph;
10 |
11 | public TinkerGraphIndex(final String key, final TinkerGraph graph) {
12 | super(key);
13 | this.graph = graph;
14 | }
15 |
16 | @Override
17 | protected boolean isAutomatic() {
18 | return true;
19 | }
20 |
21 | @Override
22 | public void initialize() {
23 | checkIsProperIndexKey(key);
24 |
25 | if (!keyExists(key)) {
26 | graph.createIndex(key, Vertex.class);
27 | }
28 | }
29 |
30 | @Override
31 | public void addInternal(Vertex vertex) {
32 | throw new UnsupportedOperationException();
33 | }
34 |
35 | @Override
36 | public void removeInternal(final Vertex vertex) {
37 | throw new UnsupportedOperationException();
38 | }
39 |
40 | private boolean keyExists(final String key) {
41 | return graph.getIndexedKeys(Vertex.class).contains(key);
42 | }
43 |
44 | private void checkIsProperIndexKey(final String key) {
45 | Preconditions.checkNotNull(key);
46 | Preconditions.checkArgument(key.length() > 0);
47 | }
48 | }
49 |
--------------------------------------------------------------------------------
/src/test/java/net/fortytwo/tpop/sail/GetAndRetrieveStatementsTest.java:
--------------------------------------------------------------------------------
1 | package net.fortytwo.tpop.sail;
2 |
3 | import org.eclipse.rdf4j.model.BNode;
4 | import org.eclipse.rdf4j.model.vocabulary.RDF;
5 | import org.junit.Test;
6 |
7 | import static org.junit.Assert.assertEquals;
8 |
9 | public class GetAndRetrieveStatementsTest extends GraphSailTestBase {
10 |
11 | @Test
12 | public void firstTest() {
13 | createConnection();
14 |
15 | assertEquals(0, countStatements());
16 | connection.addStatement(RDF.TYPE, RDF.TYPE, RDF.TYPE);
17 | assertEquals(1, countStatements());
18 | }
19 |
20 | @Test
21 | public void testTmp() {
22 | for (int i = 0; i < 10; i++) {
23 | BNode node = graphSail.getValueFactory().createBNode();
24 | System.out.println("node: " + node.stringValue() + " " + node.getID() + " " + node.toString());
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/test/java/net/fortytwo/tpop/sail/GraphSailTest.java:
--------------------------------------------------------------------------------
1 | package net.fortytwo.tpop.sail;
2 |
3 | import net.fortytwo.tpop.sail.tg.TinkerGraphIndex;
4 | import org.apache.tinkerpop.gremlin.structure.Graph;
5 | import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph;
6 | import org.eclipse.rdf4j.common.iteration.CloseableIteration;
7 | import org.eclipse.rdf4j.sail.Sail;
8 |
9 | import java.util.function.Function;
10 |
11 | public class GraphSailTest extends SailTest {
12 |
13 | @Override
14 | protected void before() throws Exception {
15 | }
16 |
17 | @Override
18 | protected void after() throws Exception {
19 | }
20 |
21 | @Override
22 | protected Sail createSail() throws Exception {
23 | GraphWrapper wrapper = createGraphWrapper();
24 | GraphSail sail = new GraphSail(wrapper.getGraph(), wrapper.getIndexFactory());
25 | sail.enforceUniqueStatements(uniqueStatements);
26 | return sail;
27 | }
28 |
29 | // override me for alternative graph back-ends
30 | protected GraphWrapper createGraphWrapper() {
31 | return createTinkerGraphWrapper();
32 | }
33 |
34 | private GraphWrapper createTinkerGraphWrapper() {
35 | TinkerGraph graph = TinkerGraph.open();
36 | return new GraphWrapper(graph, key -> new TinkerGraphIndex(key, graph));
37 | }
38 |
39 | protected static int countIterator(final CloseableIteration iter) throws X {
40 | int count = 0;
41 | while (iter.hasNext()) {
42 | iter.next();
43 | count++;
44 | }
45 | iter.close();
46 | return count;
47 | }
48 |
49 | private static class GraphWrapper {
50 | private final Graph graph;
51 | private final Function indexFactory;
52 |
53 | private GraphWrapper(final Graph graph, final Function indexFactory) {
54 | this.graph = graph;
55 | this.indexFactory = indexFactory;
56 | }
57 |
58 | public Graph getGraph() {
59 | return graph;
60 | }
61 |
62 | public Function getIndexFactory() {
63 | return indexFactory;
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/src/test/java/net/fortytwo/tpop/sail/GraphSailTestBase.java:
--------------------------------------------------------------------------------
1 | package net.fortytwo.tpop.sail;
2 |
3 | import net.fortytwo.tpop.sail.tg.TinkerGraphIndex;
4 | import org.apache.tinkerpop.gremlin.structure.Graph;
5 | import org.apache.tinkerpop.gremlin.tinkergraph.structure.TinkerGraph;
6 | import org.eclipse.rdf4j.common.iteration.CloseableIteration;
7 | import org.eclipse.rdf4j.sail.SailConnection;
8 | import org.junit.After;
9 | import org.junit.Before;
10 |
11 | import java.util.function.Function;
12 |
13 | public class GraphSailTestBase {
14 |
15 | protected GraphSail graphSail;
16 | protected SailConnection connection;
17 |
18 | @Before
19 | public void setUp() {
20 | graphSail = createSail();
21 | graphSail.initialize();
22 | }
23 |
24 | @After
25 | public void tearDown() {
26 | graphSail.shutDown();
27 | }
28 |
29 | protected GraphSail createSail() {
30 | GraphWrapper wrapper = createGraphWrapper();
31 | return new GraphSail(wrapper.getGraph(), wrapper.getIndexFactory());
32 | }
33 |
34 | // override me for alternative graph back-ends
35 | protected GraphWrapper createGraphWrapper() {
36 | return createTinkerGraphWrapper();
37 | }
38 |
39 | protected void createConnection() {
40 | connection = graphSail.getConnection();
41 | }
42 |
43 | protected int countStatements() {
44 | return countIterator(connection.getStatements(null, null, null, false));
45 | }
46 |
47 | private GraphWrapper createTinkerGraphWrapper() {
48 | TinkerGraph graph = TinkerGraph.open();
49 | return new GraphWrapper(graph, key -> new TinkerGraphIndex(key, graph));
50 | }
51 |
52 | protected static int countIterator(final CloseableIteration iter) throws X {
53 | int count = 0;
54 | while (iter.hasNext()) {
55 | iter.next();
56 | count++;
57 | }
58 | iter.close();
59 | return count;
60 | }
61 |
62 | private static class GraphWrapper {
63 | private final Graph graph;
64 | private final Function indexFactory;
65 |
66 | private GraphWrapper(final Graph graph, final Function indexFactory) {
67 | this.graph = graph;
68 | this.indexFactory = indexFactory;
69 | }
70 |
71 | public Graph getGraph() {
72 | return graph;
73 | }
74 |
75 | public Function getIndexFactory() {
76 | return indexFactory;
77 | }
78 | }
79 | }
80 |
--------------------------------------------------------------------------------
/src/test/java/net/fortytwo/tpop/sail/NamespaceStoreTest.java:
--------------------------------------------------------------------------------
1 | package net.fortytwo.tpop.sail;
2 |
3 | import org.junit.Before;
4 | import org.junit.Test;
5 |
6 | import static org.junit.Assert.assertEquals;
7 | import static org.junit.Assert.assertNull;
8 |
9 | public class NamespaceStoreTest extends GraphSailTestBase {
10 | private static final String EX_PREFIX = "ex";
11 | private static final String EX_NAME = "http://example.org/";
12 |
13 | private NamespaceStore namespaces;
14 |
15 | @Before
16 | public void setUp() {
17 | namespaces = new NamespaceStore();
18 | }
19 |
20 | @Test
21 | public void addAndGetNameSpaceIsSuccessful() {
22 | assertEquals(0, countNamespaces());
23 | namespaces.set(EX_PREFIX, EX_NAME);
24 | assertEquals(1, countNamespaces());
25 | assertEquals(EX_NAME, namespaces.get(EX_PREFIX));
26 | assertEquals(EX_PREFIX, namespaces.getAll().next().getPrefix());
27 | assertEquals(EX_NAME, namespaces.getAll().next().getName());
28 | namespaces.set(EX_PREFIX + "2", EX_NAME + "2");
29 | assertEquals(2, countNamespaces());
30 | }
31 |
32 | @Test(expected = NullPointerException.class)
33 | public void addNullPrefixFails() {
34 | namespaces.set(null, EX_NAME);
35 | }
36 |
37 | @Test(expected = NullPointerException.class)
38 | public void addNullNameFails() {
39 | namespaces.set(EX_PREFIX, null);
40 | }
41 |
42 | @Test
43 | public void nameForNonexistentPrefixIsNull() {
44 | assertNull(namespaces.get("foo"));
45 | }
46 | @Test
47 | public void getAllFromEmptyStoreIsTrivial() {
48 | assertEquals(0, countNamespaces());
49 | }
50 |
51 | @Test(expected = NullPointerException.class)
52 | public void getNullPrefixFails() {
53 | namespaces.get(null);
54 | }
55 |
56 | @Test
57 | public void emptyPrefixIsAllowed() {
58 | assertNull(namespaces.get(""));
59 | namespaces.set("", EX_NAME);
60 | assertEquals(EX_NAME, namespaces.get(""));
61 | namespaces.set("", EX_NAME + "2");
62 | assertEquals(EX_NAME + "2", namespaces.get(""));
63 | }
64 |
65 | @Test
66 | public void emptyNameIsAllowed() {
67 | namespaces.set(EX_PREFIX, "");
68 | assertEquals("", namespaces.get(EX_PREFIX));
69 | }
70 |
71 | @Test
72 | public void duplicateNamesAreAllowed() {
73 | namespaces.set(EX_PREFIX, EX_NAME);
74 | namespaces.set(EX_PREFIX + "2", EX_NAME);
75 | assertEquals(2, countNamespaces());
76 | assertEquals(EX_NAME, namespaces.get(EX_PREFIX));
77 | assertEquals(EX_NAME, namespaces.get(EX_PREFIX + "2"));
78 | }
79 |
80 | @Test
81 | public void duplicatePrefixOverridesPreviousName() {
82 | namespaces.set(EX_PREFIX, EX_NAME);
83 | assertEquals(EX_NAME, namespaces.get(EX_PREFIX));
84 | namespaces.set(EX_PREFIX, EX_NAME + "2");
85 | assertEquals(EX_NAME + "2", namespaces.get(EX_PREFIX));
86 | assertEquals(1, countNamespaces());
87 | }
88 |
89 | @Test
90 | public void prefixesAndNamesAreTrimmed() {
91 | namespaces.set(" " + EX_PREFIX, EX_NAME + " \t");
92 | assertEquals(EX_NAME, namespaces.get(EX_PREFIX));
93 | }
94 |
95 | private int countNamespaces() {
96 | return countIterator(namespaces.getAll());
97 | }
98 | }
99 |
--------------------------------------------------------------------------------
/src/test/java/net/fortytwo/tpop/sail/SailAdder.java:
--------------------------------------------------------------------------------
1 | package net.fortytwo.tpop.sail;
2 |
3 | import org.eclipse.rdf4j.model.Resource;
4 | import org.eclipse.rdf4j.model.Statement;
5 | import org.eclipse.rdf4j.rio.RDFHandler;
6 | import org.eclipse.rdf4j.rio.RDFHandlerException;
7 | import org.eclipse.rdf4j.sail.SailConnection;
8 | import org.eclipse.rdf4j.sail.SailException;
9 |
10 | class SailAdder implements RDFHandler {
11 | private final SailConnection c;
12 | private final Resource[] contexts;
13 |
14 | public SailAdder(final SailConnection c,
15 | final Resource... contexts) {
16 | this.c = c;
17 | this.contexts = contexts;
18 | }
19 |
20 | public void startRDF() throws RDFHandlerException {
21 | }
22 |
23 | public void endRDF() throws RDFHandlerException {
24 | }
25 |
26 | public void handleNamespace(final String prefix,
27 | final String uri) throws RDFHandlerException {
28 | try {
29 | c.setNamespace(prefix, uri);
30 | } catch (SailException e) {
31 | throw new RDFHandlerException(e);
32 | }
33 | }
34 |
35 | public void handleStatement(final Statement s) throws RDFHandlerException {
36 | try {
37 | if (1 <= contexts.length) {
38 | for (Resource x : contexts) {
39 | c.addStatement(s.getSubject(), s.getPredicate(), s.getObject(), x);
40 | }
41 | } else {
42 | c.addStatement(s.getSubject(), s.getPredicate(), s.getObject(), s.getContext());
43 | }
44 | } catch (SailException e) {
45 | throw new RDFHandlerException(e);
46 | }
47 | }
48 |
49 | public void handleComment(String s) throws RDFHandlerException {
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/test/java/net/fortytwo/tpop/sail/SailTest.java:
--------------------------------------------------------------------------------
1 | package net.fortytwo.tpop.sail;
2 |
3 |
4 | import com.google.common.base.Preconditions;
5 | import org.eclipse.rdf4j.common.iteration.CloseableIteration;
6 | import org.eclipse.rdf4j.model.BNode;
7 | import org.eclipse.rdf4j.model.Literal;
8 | import org.eclipse.rdf4j.model.Namespace;
9 | import org.eclipse.rdf4j.model.Resource;
10 | import org.eclipse.rdf4j.model.Statement;
11 | import org.eclipse.rdf4j.model.IRI;
12 | import org.eclipse.rdf4j.model.Value;
13 | import org.eclipse.rdf4j.model.ValueFactory;
14 | import org.eclipse.rdf4j.model.datatypes.XMLDatatypeUtil;
15 | import org.eclipse.rdf4j.model.vocabulary.RDF;
16 | import org.eclipse.rdf4j.model.vocabulary.RDFS;
17 | import org.eclipse.rdf4j.model.vocabulary.XMLSchema;
18 | import org.eclipse.rdf4j.query.BindingSet;
19 | import org.eclipse.rdf4j.query.QueryEvaluationException;
20 | import org.eclipse.rdf4j.query.impl.EmptyBindingSet;
21 | import org.eclipse.rdf4j.query.parser.ParsedQuery;
22 | import org.eclipse.rdf4j.query.parser.sparql.SPARQLParser;
23 | import org.eclipse.rdf4j.rio.RDFFormat;
24 | import org.eclipse.rdf4j.rio.RDFHandler;
25 | import org.eclipse.rdf4j.rio.RDFParser;
26 | import org.eclipse.rdf4j.rio.Rio;
27 | import org.eclipse.rdf4j.sail.NotifyingSail;
28 | import org.eclipse.rdf4j.sail.NotifyingSailConnection;
29 | import org.eclipse.rdf4j.sail.Sail;
30 | import org.eclipse.rdf4j.sail.SailChangedEvent;
31 | import org.eclipse.rdf4j.sail.SailChangedListener;
32 | import org.eclipse.rdf4j.sail.SailConnection;
33 | import org.eclipse.rdf4j.sail.SailConnectionListener;
34 | import org.eclipse.rdf4j.sail.SailException;
35 | import org.eclipse.rdf4j.sail.inferencer.InferencerConnection;
36 | import org.eclipse.rdf4j.sail.inferencer.fc.ForwardChainingRDFSInferencer;
37 | import org.junit.After;
38 | import org.junit.Before;
39 | import org.junit.Ignore;
40 | import org.junit.Test;
41 |
42 | import javax.xml.datatype.XMLGregorianCalendar;
43 | import java.io.InputStream;
44 | import java.util.Collection;
45 | import java.util.HashSet;
46 | import java.util.Iterator;
47 | import java.util.LinkedList;
48 | import java.util.Set;
49 |
50 | import static org.junit.Assert.assertEquals;
51 | import static org.junit.Assert.assertFalse;
52 | import static org.junit.Assert.assertNotEquals;
53 | import static org.junit.Assert.assertNotNull;
54 | import static org.junit.Assert.assertNull;
55 | import static org.junit.Assert.assertTrue;
56 |
57 | /**
58 | * @author Joshua Shinavier (http://fortytwo.net)
59 | */
60 | public abstract class SailTest {
61 | protected Sail sail;
62 | protected ForwardChainingRDFSInferencer inferencer;
63 |
64 | protected boolean uniqueStatements = false;
65 |
66 | @Before
67 | public final void setUp() throws Exception {
68 | before();
69 | this.sail = createSail();
70 | sail.initialize();
71 |
72 | if (sail instanceof NotifyingSail) {
73 | try (SailConnection sc = getConnection()) {
74 | if (sc instanceof InferencerConnection) {
75 | inferencer = new ForwardChainingRDFSInferencer((NotifyingSail) sail);
76 | }
77 | }
78 | }
79 | }
80 |
81 | @After
82 | public final void tearDown() throws Exception {
83 | sail.shutDown();
84 | after();
85 | }
86 |
87 | protected abstract void before() throws Exception;
88 |
89 | protected abstract void after() throws Exception;
90 |
91 | protected abstract Sail createSail() throws Exception;
92 |
93 | // statement manipulation //////////////////////////////////////////////////
94 |
95 | @Test
96 | public void testGetStatementsS_POG() throws Exception {
97 | try (SailConnection sc = getConnection()) {
98 | IRI uriA = sail.getValueFactory().createIRI("http://example.org/test/S_POG#a");
99 | IRI uriB = sail.getValueFactory().createIRI("http://example.org/test/S_POG#b");
100 | IRI uriC = sail.getValueFactory().createIRI("http://example.org/test/S_POG#c");
101 | IRI uriD = sail.getValueFactory().createIRI("http://example.org/test/S_POG#d");
102 | long before, after;
103 |
104 | // default context, different S,P,O
105 | sc.removeStatements(uriA, null, null);
106 | commit(sc);
107 | before = countStatements(sc, uriA, null, null, false);
108 | sc.addStatement(uriA, uriB, uriC);
109 | commit(sc);
110 | after = countStatements(sc, uriA, null, null, false);
111 | assertEquals(0, before);
112 | System.out.flush();
113 | assertEquals(1, after);
114 |
115 | // one specific context, different S,P,O
116 | sc.removeStatements(uriA, null, null, uriD);
117 | commit(sc);
118 | before = countStatements(sc, uriA, null, null, false, uriD);
119 | sc.addStatement(uriA, uriB, uriC, uriD);
120 | commit(sc);
121 | after = countStatements(sc, uriA, null, null, false, uriD);
122 | assertEquals(0, before);
123 | assertEquals(1, after);
124 |
125 | // one specific context, same S,P,O,G
126 | sc.removeStatements(uriA, null, null, uriA);
127 | commit(sc);
128 | before = countStatements(sc, uriA, null, null, false, uriA);
129 | sc.addStatement(uriA, uriB, uriC, uriA);
130 | commit(sc);
131 | after = countStatements(sc, uriA, null, null, false, uriA);
132 | assertEquals(0, before);
133 | assertEquals(1, after);
134 |
135 | // default context, same S,P,O
136 | sc.removeStatements(uriA, null, null);
137 | commit(sc);
138 | before = countStatements(sc, uriA, null, null, false);
139 | sc.addStatement(uriA, uriB, uriC);
140 | commit(sc);
141 | after = countStatements(sc, uriA, null, null, false);
142 | assertEquals(0, before);
143 | assertEquals(1, after);
144 | }
145 | }
146 |
147 | @Test
148 | public void testGetStatementsSP_OG() throws Exception {
149 | try (SailConnection sc = getConnection()) {
150 | IRI uriA = sail.getValueFactory().createIRI("http://example.org/test/SP_OG#a");
151 | IRI uriB = sail.getValueFactory().createIRI("http://example.org/test/SP_OG#b");
152 | IRI uriC = sail.getValueFactory().createIRI("http://example.org/test/SP_OG#c");
153 | long before, after;
154 |
155 | // Add statement to the implicit null context.
156 | sc.removeStatements(null, null, null);
157 | before = countStatements(sc, uriA, uriB, null, false);
158 | sc.addStatement(uriA, uriB, uriC);
159 | commit(sc);
160 | after = countStatements(sc, uriA, uriB, null, false);
161 | assertEquals(0, before);
162 | assertEquals(1, after);
163 | }
164 | }
165 |
166 | @Test
167 | public void testGetStatementsO_SPG() throws Exception {
168 | try (SailConnection sc = getConnection()) {
169 | IRI uriA = sail.getValueFactory().createIRI("http://example.org/test/O_SPG#a");
170 | IRI uriB = sail.getValueFactory().createIRI("http://example.org/test/O_SPG#b");
171 | IRI uriC = sail.getValueFactory().createIRI("http://example.org/test/O_SPG#c");
172 | Literal plainLitA = sail.getValueFactory().createLiteral("arbitrary plain literal 9548734867");
173 | Literal stringLitA = sail.getValueFactory().createLiteral("arbitrary string literal 8765", XMLSchema.STRING);
174 | long before, after;
175 |
176 | // Add statement to a specific context.
177 | sc.removeStatements(null, null, uriA, uriA);
178 | commit(sc);
179 | before = countStatements(sc, null, null, uriA, false);
180 | sc.addStatement(uriB, uriC, uriA);
181 | commit(sc);
182 | after = countStatements(sc, null, null, uriA, false);
183 | assertEquals(0, before);
184 | assertEquals(1, after);
185 |
186 | // Add plain literal statement to the default context.
187 | sc.removeStatements(null, null, plainLitA);
188 | commit(sc);
189 | before = countStatements(sc, null, null, plainLitA, false);
190 | sc.addStatement(uriA, uriA, plainLitA);
191 | commit(sc);
192 | after = countStatements(sc, null, null, plainLitA, false);
193 | assertEquals(0, before);
194 | assertEquals(1, after);
195 |
196 | // Add string-typed literal statement to the default context.
197 | sc.removeStatements(null, null, plainLitA);
198 | commit(sc);
199 | before = countStatements(sc, null, null, stringLitA, false);
200 | sc.addStatement(uriA, uriA, stringLitA);
201 | commit(sc);
202 | after = countStatements(sc, null, null, stringLitA, false);
203 | assertEquals(0, before);
204 | assertEquals(1, after);
205 | }
206 | }
207 |
208 | @Test
209 | public void testGetStatementsPO_SG() throws Exception {
210 | try (SailConnection sc = getConnection()) {
211 | IRI uriA = sail.getValueFactory().createIRI("http://example.org/test/PO_SG#a");
212 | IRI uriB = sail.getValueFactory().createIRI("http://example.org/test/PO_SG#b");
213 | IRI foo = sail.getValueFactory().createIRI("http://example.org/ns#foo");
214 | IRI firstName = sail.getValueFactory().createIRI("http://example.org/ns#firstName");
215 | Literal plainLitA = sail.getValueFactory().createLiteral("arbitrary plain literal 8765675");
216 | Literal fooLabel = sail.getValueFactory().createLiteral("foo", XMLSchema.STRING);
217 | long before, after;
218 |
219 | // Add statement to the implicit null context.
220 | sc.removeStatements(null, null, null, uriA);
221 | commit(sc);
222 | before = countStatements(sc, null, uriA, uriB, false);
223 | sc.addStatement(uriA, uriA, uriB);
224 | commit(sc);
225 | after = countStatements(sc, null, uriA, uriB, false);
226 | assertEquals(0, before);
227 | assertEquals(1, after);
228 |
229 | // Add plain literal statement to the default context.
230 | sc.removeStatements(null, null, plainLitA);
231 | commit(sc);
232 | before = countStatements(sc, null, uriA, plainLitA, false);
233 | sc.addStatement(uriA, uriA, plainLitA);
234 | sc.addStatement(uriA, uriB, plainLitA);
235 | sc.addStatement(uriB, uriB, plainLitA);
236 | commit(sc);
237 | after = countStatements(sc, null, uriA, plainLitA, false);
238 | assertEquals(0, before);
239 | assertEquals(1, after);
240 |
241 | // Add string-typed literal statement to the default context.
242 | sc.removeStatements(null, null, fooLabel);
243 | commit(sc);
244 | before = countStatements(sc, null, firstName, fooLabel, false);
245 | sc.addStatement(foo, firstName, fooLabel);
246 | commit(sc);
247 | after = countStatements(sc, null, firstName, fooLabel, false);
248 | assertEquals(0, before);
249 | assertEquals(1, after);
250 | assertEquals(foo, toSet(sc.getStatements(null, firstName, fooLabel, false)).iterator().next().getSubject());
251 |
252 | }
253 | }
254 |
255 | @Test
256 | public void testGetStatementsSPO_G() throws Exception {
257 | try (SailConnection sc = getConnection()) {
258 | IRI uriA = sail.getValueFactory().createIRI("http://example.org/test/S_POG#a");
259 | IRI uriB = sail.getValueFactory().createIRI("http://example.org/test/S_POG#b");
260 | IRI uriC = sail.getValueFactory().createIRI("http://example.org/test/S_POG#c");
261 | IRI uriD = sail.getValueFactory().createIRI("http://example.org/test/S_POG#d");
262 | long before, after;
263 |
264 | // default context, different S,P,O
265 | sc.removeStatements(uriA, null, null);
266 | commit(sc);
267 | before = countStatements(sc, uriA, uriB, uriC, false);
268 | sc.addStatement(uriA, uriB, uriC);
269 | commit(sc);
270 | after = countStatements(sc, uriA, uriB, uriC, false);
271 | assertEquals(0, before);
272 | assertEquals(1, after);
273 |
274 | // default context, same S,P,O
275 | sc.removeStatements(uriA, null, null);
276 | commit(sc);
277 | before = countStatements(sc, uriA, uriB, uriC, false);
278 | sc.addStatement(uriA, uriB, uriC);
279 | commit(sc);
280 | after = countStatements(sc, uriA, uriB, uriC, false);
281 | assertEquals(0, before);
282 | assertEquals(1, after);
283 |
284 | // one specific context, different S,P,O
285 | sc.removeStatements(uriA, null, null, uriD);
286 | commit(sc);
287 | before = countStatements(sc, uriA, uriB, uriC, false, uriD);
288 | sc.addStatement(uriA, uriB, uriC, uriD);
289 | commit(sc);
290 | after = countStatements(sc, uriA, uriB, uriC, false, uriD);
291 | assertEquals(0, before);
292 | assertEquals(1, after);
293 |
294 | // one specific context, same S,P,O,G
295 | sc.removeStatements(uriA, null, null, uriA);
296 | commit(sc);
297 | before = countStatements(sc, uriA, uriB, uriC, false, uriA);
298 | sc.addStatement(uriA, uriB, uriC, uriA);
299 | commit(sc);
300 | after = countStatements(sc, uriA, uriB, uriC, false, uriA);
301 | assertEquals(0, before);
302 | assertEquals(1, after);
303 | }
304 | }
305 |
306 | @Test
307 | public void testGetStatementsP_SOG() throws Exception {
308 | try (SailConnection sc = getConnection()) {
309 | IRI uriA = sail.getValueFactory().createIRI("http://example.org/test/P_SOG#a");
310 | IRI uriB = sail.getValueFactory().createIRI("http://example.org/test/P_SOG#b");
311 | IRI uriC = sail.getValueFactory().createIRI("http://example.org/test/P_SOG#c");
312 | IRI foo = sail.getValueFactory().createIRI("http://example.org/ns#foo");
313 | IRI firstName = sail.getValueFactory().createIRI("http://example.org/ns#firstName");
314 | Literal plainLitA = sail.getValueFactory().createLiteral("arbitrary plain literal 238445");
315 | Literal fooLabel = sail.getValueFactory().createLiteral("foo", XMLSchema.STRING);
316 | long before, after;
317 |
318 | // Add statement to the implicit null context.
319 | sc.removeStatements(null, uriA, null);
320 | commit(sc);
321 | before = countStatements(sc, null, uriA, null, false);
322 | sc.addStatement(uriB, uriA, uriC);
323 | commit(sc);
324 | after = countStatements(sc, null, uriA, null, false);
325 | assertEquals(0, before);
326 | assertEquals(1, after);
327 |
328 | // Add plain literal statement to the default context.
329 | sc.removeStatements(null, uriA, null);
330 | commit(sc);
331 | before = countStatements(sc, null, uriA, null, false);
332 | sc.addStatement(uriA, uriA, plainLitA);
333 | sc.addStatement(uriA, uriB, plainLitA);
334 | sc.addStatement(uriB, uriB, plainLitA);
335 | commit(sc);
336 | after = countStatements(sc, null, uriA, null, false);
337 | assertEquals(0, before);
338 | assertEquals(1, after);
339 |
340 | // Add string-typed literal statement to the default context.
341 | sc.removeStatements(null, firstName, null);
342 | commit(sc);
343 | before = countStatements(sc, null, firstName, null, false);
344 | sc.addStatement(foo, firstName, fooLabel);
345 | commit(sc);
346 | after = countStatements(sc, null, firstName, null, false);
347 | assertEquals(0, before);
348 | assertEquals(1, after);
349 | assertEquals(foo, toSet(sc.getStatements(null, firstName, null, false)).iterator().next().getSubject());
350 |
351 | // Add statement to a non-null context.
352 | sc.removeStatements(null, uriA, null);
353 | commit(sc);
354 | before = countStatements(sc, null, uriA, null, false);
355 | sc.addStatement(uriB, uriA, uriC, uriA);
356 | commit(sc);
357 | after = countStatements(sc, null, uriA, null, false);
358 | assertEquals(0, before);
359 | assertEquals(1, after);
360 |
361 | sc.removeStatements(null, uriA, null);
362 | commit(sc);
363 | before = countStatements(sc, null, uriA, null, false);
364 | sc.addStatement(uriB, uriA, uriC, uriC);
365 | sc.addStatement(uriC, uriA, uriA, uriA);
366 | commit(sc);
367 | sc.addStatement(uriA, uriA, uriB, uriB);
368 | commit(sc);
369 | after = countStatements(sc, null, uriA, null, false);
370 | assertEquals(0, before);
371 | assertEquals(3, after);
372 | }
373 | }
374 |
375 | @Test
376 | public void testGetStatementsWithVariableContexts() throws Exception {
377 | try (SailConnection sc = getConnection()) {
378 | IRI uriA = sail.getValueFactory().createIRI("http://example.org/uriA");
379 | IRI uriB = sail.getValueFactory().createIRI("http://example.org/uriB");
380 | IRI uriC = sail.getValueFactory().createIRI("http://example.org/uriC");
381 | long count;
382 | sc.clear();
383 | //sc.removeStatements(uriA, uriA, uriA);
384 | commit(sc);
385 | Resource[] contexts = {uriA, null};
386 | sc.addStatement(uriA, uriB, uriC, contexts);
387 | commit(sc);
388 |
389 | // Get statements from all contexts.
390 | count = countStatements(sc, uriA, null, null, false);
391 | assertEquals(2, count);
392 |
393 | // Get statements from a specific partition context.
394 | count = countStatements(sc, null, null, null, false, uriA);
395 | assertEquals(1, count);
396 |
397 | // Get statements from the null context.
398 | Resource[] c = {null};
399 | count = countStatements(sc, null, null, null, false, c);
400 | //assertTrue(count > 0);
401 | assertEquals(1, count);
402 | long countLast = count;
403 |
404 | // Get statements from more than one context.
405 | count = countStatements(sc, null, null, null, false, contexts);
406 | assertEquals(1 + countLast, count);
407 |
408 | }
409 | }
410 |
411 | @Test
412 | public void testRemoveStatements() throws Exception {
413 | try (SailConnection sc = getConnection()) {
414 | IRI uriA = sail.getValueFactory().createIRI("http://example.org/uriA");
415 | IRI uriB = sail.getValueFactory().createIRI("http://example.org/uriB");
416 | IRI uriC = sail.getValueFactory().createIRI("http://example.org/uriC");
417 | Resource[] contexts = {uriA, null};
418 | long count;
419 |
420 | // Remove from all contexts.
421 | sc.removeStatements(uriA, null, null);
422 | commit(sc);
423 | count = countStatements(sc, uriA, null, null, false);
424 | assertEquals(0, count);
425 | sc.addStatement(uriA, uriB, uriC, contexts);
426 | commit(sc);
427 | count = countStatements(sc, uriA, null, null, false);
428 | // two, because the statement was added to two contexts
429 | assertEquals(2, count);
430 | sc.removeStatements(uriA, null, null);
431 | commit(sc);
432 | count = countStatements(sc, uriA, null, null, false);
433 | assertEquals(0, count);
434 |
435 | // Remove from one partition context.
436 | sc.removeStatements(uriA, null, null);
437 | commit(sc);
438 | count = countStatements(sc, uriA, null, null, false);
439 | assertEquals(0, count);
440 | sc.addStatement(uriA, uriB, uriC, contexts);
441 | commit(sc);
442 | count = countStatements(sc, uriA, null, null, false);
443 | assertEquals(2, count);
444 | Resource[] oneContext = {uriA};
445 | sc.removeStatements(uriA, null, null, oneContext);
446 | commit(sc);
447 | count = countStatements(sc, uriA, null, null, false);
448 | assertEquals(1, count);
449 |
450 | // Remove from the null context.
451 | sc.removeStatements(uriA, null, null);
452 | commit(sc);
453 | count = countStatements(sc, uriA, null, null, false);
454 | assertEquals(0, count);
455 | sc.addStatement(uriA, uriB, uriC, contexts);
456 | commit(sc);
457 | count = countStatements(sc, uriA, null, null, false);
458 | assertEquals(2, count);
459 | Resource[] nullContext = {null};
460 | sc.removeStatements(uriA, null, null, nullContext);
461 | commit(sc);
462 | count = countStatements(sc, uriA, null, null, false);
463 | assertEquals(1, count);
464 |
465 | // Remove from more than one context.
466 | sc.removeStatements(uriA, null, null);
467 | commit(sc);
468 | count = countStatements(sc, uriA, null, null, false);
469 | assertEquals(0, count);
470 | sc.addStatement(uriA, uriB, uriC, contexts);
471 | commit(sc);
472 | count = countStatements(sc, uriA, null, null, false);
473 | assertEquals(2, count);
474 | sc.removeStatements(uriA, null, null);
475 | commit(sc);
476 | count = countStatements(sc, uriA, null, null, false, contexts);
477 | assertEquals(0, count);
478 | }
479 | }
480 |
481 | @Test
482 | public void testClear() throws Exception {
483 | IRI uriA = sail.getValueFactory().createIRI("http://example.org/uriA");
484 | IRI uriB = sail.getValueFactory().createIRI("http://example.org/uriB");
485 | IRI uriC = sail.getValueFactory().createIRI("http://example.org/uriC");
486 |
487 | try (SailConnection sc = getConnection()) {
488 | sc.clear();
489 | assertEquals(0L, sc.size());
490 | sc.addStatement(uriA, uriB, uriC, uriA);
491 | sc.addStatement(uriC, uriA, uriB, uriA);
492 | sc.addStatement(uriB, uriC, uriA, uriA);
493 | assertEquals(3L, sc.size(uriA));
494 | sc.addStatement(uriA, uriB, uriC, uriB);
495 | sc.addStatement(uriB, uriC, uriA, uriB);
496 | assertEquals(2L, sc.size(uriB));
497 | sc.addStatement(uriA, uriB, uriC);
498 | assertEquals(1L, sc.size((Resource) null));
499 | sc.addStatement(uriA, uriB, uriC, uriC);
500 | sc.addStatement(uriB, uriC, uriA, uriC);
501 | sc.addStatement(uriC, uriA, uriB, uriC);
502 | sc.addStatement(uriA, uriB, uriB, uriC);
503 | assertEquals(4L, sc.size(uriC));
504 | assertEquals(10L, sc.size());
505 | sc.clear(uriA, uriC);
506 | assertEquals(1L, sc.size((Resource) null));
507 | assertEquals(0L, sc.size(uriA));
508 | assertEquals(2L, sc.size(uriB));
509 | assertEquals(0L, sc.size(uriC));
510 | assertEquals(3L, sc.size());
511 | sc.clear();
512 | assertEquals(0L, sc.size());
513 | commit(sc);
514 | }
515 | }
516 |
517 | @Test
518 | public void testGetContextIDs() throws Exception {
519 | // TODO
520 | }
521 |
522 | @Test
523 | public void testSize() throws Exception {
524 | IRI uriA = sail.getValueFactory().createIRI("http://example.org/uriA");
525 | IRI uriB = sail.getValueFactory().createIRI("http://example.org/uriB");
526 | IRI uriC = sail.getValueFactory().createIRI("http://example.org/uriC");
527 |
528 | try (SailConnection sc = getConnection()) {
529 | sc.removeStatements(null, null, null);
530 |
531 | assertEquals(0L, sc.size());
532 | sc.addStatement(uriA, uriB, uriC, uriA);
533 | // commit(sc);
534 | for (Statement st : IterUtils.collect(sc.getStatements(null, null, null, false))) {
535 | System.out.println("st: " + st);
536 | }
537 | assertEquals(1L, sc.size());
538 | sc.addStatement(uriA, uriB, uriC, uriB);
539 | // commit(sc);
540 | assertEquals(2L, sc.size());
541 | sc.addStatement(uriB, uriB, uriC, uriB);
542 | // commit(sc);
543 | assertEquals(3L, sc.size());
544 | sc.addStatement(uriC, uriB, uriA);
545 | // commit(sc);
546 | assertEquals(4L, sc.size());
547 | assertEquals(1L, sc.size(uriA));
548 | assertEquals(2L, sc.size(uriB));
549 | assertEquals(0L, sc.size(uriC));
550 | assertEquals(1L, sc.size((IRI) null));
551 | assertEquals(3L, sc.size(uriB, null));
552 | assertEquals(3L, sc.size(uriB, uriC, null));
553 | assertEquals(4L, sc.size(uriA, uriB, null));
554 | assertEquals(4L, sc.size(uriA, uriB, uriC, null));
555 | assertEquals(3L, sc.size(uriA, uriB));
556 | commit(sc);
557 | }
558 | }
559 |
560 | @Test
561 | public void testDuplicateStatements() throws Exception {
562 | if (uniqueStatements) {
563 | IRI uriA = sail.getValueFactory().createIRI("http://example.org/uriA");
564 | IRI uriB = sail.getValueFactory().createIRI("http://example.org/uriB");
565 | IRI uriC = sail.getValueFactory().createIRI("http://example.org/uriC");
566 | try (SailConnection sc = getConnection()) {
567 | sc.clear();
568 | assertEquals(0, countStatements(sc, uriA, uriB, uriC, false));
569 | sc.addStatement(uriA, uriB, uriC);
570 | assertEquals(1, countStatements(sc, uriA, uriB, uriC, false));
571 | sc.addStatement(uriA, uriB, uriC);
572 | assertEquals(1, countStatements(sc, uriA, uriB, uriC, false));
573 |
574 | sc.addStatement(uriA, uriB, uriC, uriC);
575 | assertEquals(2, countStatements(sc, uriA, uriB, uriC, false));
576 | assertEquals(1, countStatements(sc, uriA, uriB, uriC, false, uriC));
577 | commit(sc);
578 | }
579 | }
580 | }
581 |
582 | // IRIs ////////////////////////////////////////////////////////////////////
583 |
584 | // literals ////////////////////////////////////////////////////////////////
585 |
586 | // Note: this test will always pass as long as we're using ValueFactoryImpl
587 |
588 | @Test
589 | public void testCreateLiteralsThroughValueFactory() throws Exception {
590 | Literal l;
591 | ValueFactory vf = sail.getValueFactory();
592 |
593 | l = vf.createLiteral("a plain literal");
594 | assertNotNull(l);
595 | assertFalse(l.getLanguage().isPresent());
596 | assertEquals("a plain literal", l.getLabel());
597 | assertEquals(XMLSchema.STRING, l.getDatatype());
598 | l = vf.createLiteral("auf Deutsch, bitte", "de");
599 | assertNotNull(l);
600 | assertEquals("de", l.getLanguage().get());
601 | assertEquals("auf Deutsch, bitte", l.getLabel());
602 | assertEquals(RDF.LANGSTRING, l.getDatatype());
603 |
604 | // Test data-typed createLiteral methods
605 | l = vf.createLiteral("foo", XMLSchema.STRING);
606 | assertNotNull(l);
607 | assertFalse(l.getLanguage().isPresent());
608 | assertEquals("foo", l.getLabel());
609 | assertEquals(XMLSchema.STRING, l.getDatatype());
610 | l = vf.createLiteral(42);
611 | assertNotNull(l);
612 | assertFalse(l.getLanguage().isPresent());
613 | assertEquals("42", l.getLabel());
614 | assertEquals(42, l.intValue());
615 | assertEquals(XMLSchema.INT, l.getDatatype());
616 | l = vf.createLiteral(42L);
617 | assertNotNull(l);
618 | assertFalse(l.getLanguage().isPresent());
619 | assertEquals("42", l.getLabel());
620 | assertEquals(42L, l.longValue());
621 | assertEquals(XMLSchema.LONG, l.getDatatype());
622 | l = vf.createLiteral((short) 42);
623 | assertNotNull(l);
624 | assertFalse(l.getLanguage().isPresent());
625 | assertEquals("42", l.getLabel());
626 | assertEquals((short) 42, l.shortValue());
627 | assertEquals(XMLSchema.SHORT, l.getDatatype());
628 | l = vf.createLiteral(true);
629 | assertNotNull(l);
630 | assertFalse(l.getLanguage().isPresent());
631 | assertEquals("true", l.getLabel());
632 | assertEquals(true, l.booleanValue());
633 | assertEquals(XMLSchema.BOOLEAN, l.getDatatype());
634 | l = vf.createLiteral((byte) 'c');
635 | assertNotNull(l);
636 | assertFalse(l.getLanguage().isPresent());
637 | assertEquals("99", l.getLabel());
638 | assertEquals((byte) 'c', l.byteValue());
639 | assertEquals(XMLSchema.BYTE, l.getDatatype());
640 | XMLGregorianCalendar calendar = XMLDatatypeUtil.parseCalendar("2002-10-10T12:00:00-05:00");
641 | l = vf.createLiteral(calendar);
642 | assertNotNull(l);
643 | assertFalse(l.getLanguage().isPresent());
644 | assertEquals("2002-10-10T12:00:00-05:00", l.getLabel());
645 | assertEquals(calendar, l.calendarValue());
646 | assertEquals(XMLSchema.DATETIME, l.getDatatype());
647 | }
648 |
649 | @Test
650 | public void testGetLiteralsFromTripleStore() throws Exception {
651 | addTestFile();
652 |
653 | Literal l;
654 | String prefix = "urn:com.tinkerpop.blueprints.pgm.oupls.sail.test/";
655 | XMLGregorianCalendar calendar;
656 | ValueFactory vf = sail.getValueFactory();
657 | SailConnection sc = getConnection();
658 |
659 | // Get an actual plain literal from the triple store.
660 | IRI ford = vf.createIRI(prefix + "ford");
661 | l = (Literal) toSet(sc.getStatements(ford, RDFS.COMMENT, null, false)).iterator().next().getObject();
662 | assertNotNull(l);
663 | assertEquals("he really knows where his towel is", l.getLabel());
664 | assertEquals(XMLSchema.STRING, l.getDatatype());
665 | assertFalse(l.getLanguage().isPresent());
666 | IRI thor = vf.createIRI(prefix + "thor");
667 |
668 | // Get an actual language-tagged literal from the triple store.
669 | IRI foafName = vf.createIRI("http://xmlns.com/foaf/0.1/name");
670 | Iterator iter = toSet(sc.getStatements(thor, foafName, null, false)).iterator();
671 | boolean found = false;
672 | while (iter.hasNext()) {
673 | l = (Literal) iter.next().getObject();
674 | if (l.getLanguage().get().equals("en")) {
675 | found = true;
676 | assertEquals("Thor", l.getLabel());
677 | assertEquals(RDF.LANGSTRING, l.getDatatype());
678 | }
679 | // if (l.getLanguage().equals("is")) {
680 | // found = true;
681 | // assertEquals("?�r", l.getLabel());
682 | // }
683 | }
684 | assertTrue(found);
685 |
686 | // Get an actual data-typed literal from the triple-store.
687 | IRI msnChatID = vf.createIRI("http://xmlns.com/foaf/0.1/msnChatID");
688 | l = (Literal) toSet(sc.getStatements(thor, msnChatID, null, false)).iterator().next().getObject();
689 | assertNotNull(l);
690 | assertFalse(l.getLanguage().isPresent());
691 | assertEquals("Thorster123", l.getLabel());
692 | assertEquals(XMLSchema.STRING, l.getDatatype());
693 |
694 | // Test Literal.xxxValue() methods for Literals read from the triple
695 | // store
696 | IRI valueUri, hasValueUri;
697 | hasValueUri = vf.createIRI(prefix + "hasValue");
698 | valueUri = vf.createIRI(prefix + "stringValue");
699 | l = (Literal) toSet(sc.getStatements(valueUri, hasValueUri, null, false)).iterator().next().getObject();
700 | assertNotNull(l);
701 | assertFalse(l.getLanguage().isPresent());
702 | assertEquals("foo", l.getLabel());
703 | assertEquals(XMLSchema.STRING, l.getDatatype());
704 | valueUri = vf.createIRI(prefix + "byteValue");
705 | l = (Literal) toSet(sc.getStatements(valueUri, hasValueUri, null, false)).iterator().next().getObject();
706 | assertNotNull(l);
707 | assertFalse(l.getLanguage().isPresent());
708 | assertEquals("99", l.getLabel());
709 | assertEquals(XMLSchema.BYTE, l.getDatatype());
710 | assertEquals((byte) 'c', l.byteValue());
711 | valueUri = vf.createIRI(prefix + "booleanValue");
712 | l = (Literal) toSet(sc.getStatements(valueUri, hasValueUri, null, false)).iterator().next().getObject();
713 | assertNotNull(l);
714 | assertFalse(l.getLanguage().isPresent());
715 | assertEquals("false", l.getLabel());
716 | assertEquals(XMLSchema.BOOLEAN, l.getDatatype());
717 | assertEquals(false, l.booleanValue());
718 | valueUri = vf.createIRI(prefix + "intValue");
719 | l = (Literal) toSet(sc.getStatements(valueUri, hasValueUri, null, false)).iterator().next().getObject();
720 | assertNotNull(l);
721 | assertFalse(l.getLanguage().isPresent());
722 | assertEquals("42", l.getLabel());
723 | assertEquals(XMLSchema.INT, l.getDatatype());
724 | assertEquals(42, l.intValue());
725 | valueUri = vf.createIRI(prefix + "shortValue");
726 | l = (Literal) toSet(sc.getStatements(valueUri, hasValueUri, null, false)).iterator().next().getObject();
727 | assertNotNull(l);
728 | assertFalse(l.getLanguage().isPresent());
729 | assertEquals("42", l.getLabel());
730 | assertEquals(XMLSchema.SHORT, l.getDatatype());
731 | assertEquals((short) 42, l.shortValue());
732 | valueUri = vf.createIRI(prefix + "longValue");
733 | l = (Literal) toSet(sc.getStatements(valueUri, hasValueUri, null, false)).iterator().next().getObject();
734 | assertNotNull(l);
735 | assertFalse(l.getLanguage().isPresent());
736 | assertEquals("42", l.getLabel());
737 | assertEquals(XMLSchema.LONG, l.getDatatype());
738 | assertEquals(42L, l.longValue());
739 | valueUri = vf.createIRI(prefix + "floatValue");
740 | l = (Literal) toSet(sc.getStatements(valueUri, hasValueUri, null, false)).iterator().next().getObject();
741 | assertNotNull(l);
742 | assertFalse(l.getLanguage().isPresent());
743 | assertEquals("3.1415926", l.getLabel());
744 | assertEquals(XMLSchema.FLOAT, l.getDatatype());
745 | assertEquals((float) 3.1415, l.floatValue(), 0.0001);
746 | valueUri = vf.createIRI(prefix + "doubleValue");
747 | l = (Literal) toSet(sc.getStatements(valueUri, hasValueUri, null, false)).iterator().next().getObject();
748 | assertNotNull(l);
749 | assertFalse(l.getLanguage().isPresent());
750 | assertEquals("3.1415926", l.getLabel());
751 | assertEquals(XMLSchema.DOUBLE, l.getDatatype());
752 | assertEquals(3.1415, l.doubleValue(), 0.0001);
753 | valueUri = vf.createIRI(prefix + "dateTimeValue");
754 | calendar = XMLDatatypeUtil.parseCalendar("2002-10-10T12:00:00-05:00");
755 | l = (Literal) toSet(sc.getStatements(valueUri, hasValueUri, null, false)).iterator().next().getObject();
756 | assertNotNull(l);
757 | assertFalse(l.getLanguage().isPresent());
758 | assertEquals("2002-10-10T12:00:00-05:00", l.getLabel());
759 | assertEquals(XMLSchema.DATETIME, l.getDatatype());
760 | assertEquals(calendar, l.calendarValue());
761 | sc.close();
762 | }
763 |
764 | // blank nodes /////////////////////////////////////////////////////////////
765 |
766 | @Test
767 | public void testBlankNodes() throws Throwable {
768 | IRI uriA = sail.getValueFactory().createIRI("http://example.org/test/S_POG#a");
769 | IRI uriB = sail.getValueFactory().createIRI("http://example.org/test/S_POG#b");
770 | try (SailConnection sc = getConnection()) {
771 | ValueFactory factory = sail.getValueFactory();
772 | BNode bNode = factory.createBNode();
773 | try {
774 | sc.addStatement(uriA, uriA, bNode);
775 | } catch (SailException se) {
776 | // FIXME: not supporting blank nodes ATM
777 | assertTrue(se.getCause() instanceof UnsupportedOperationException);
778 | }
779 | commit(sc);
780 | }
781 | }
782 |
783 | // tuple queries ///////////////////////////////////////////////////////////
784 |
785 | @Test
786 | public void testEvaluate() throws Exception {
787 | addTestFile();
788 |
789 | Set languages;
790 | String prefix = "urn:com.tinkerpop.blueprints.pgm.oupls.sail.test/";
791 | IRI thorUri = sail.getValueFactory().createIRI(prefix + "thor");
792 |
793 | try (SailConnection sc = getConnection()) {
794 | IRI uriA = sail.getValueFactory().createIRI("http://example.org/uriA");
795 | IRI uriB = sail.getValueFactory().createIRI("http://example.org/uriB");
796 | IRI uriC = sail.getValueFactory().createIRI("http://example.org/uriC");
797 | sc.addStatement(uriA, uriB, uriC);
798 | commit(sc);
799 |
800 | SPARQLParser parser = new SPARQLParser();
801 | BindingSet bindings = new EmptyBindingSet();
802 | String baseIRI = "http://example.org/bogus/";
803 | String queryStr;
804 | ParsedQuery query;
805 | CloseableIteration extends BindingSet, QueryEvaluationException> results;
806 | int count;
807 | // s ?p ?o SELECT
808 | queryStr = "SELECT ?y ?z WHERE { ?y ?z }";
809 | query = parser.parseQuery(queryStr, baseIRI);
810 | results = sc.evaluate(query.getTupleExpr(), query.getDataset(), bindings, false);
811 | count = 0;
812 | while (results.hasNext()) {
813 | count++;
814 | BindingSet set = results.next();
815 | IRI y = (IRI) set.getValue("y");
816 | Value z = set.getValue("z");
817 | assertNotNull(y);
818 | assertNotNull(z);
819 | // System.out.println("y = " + y + ", z = " + z);
820 | }
821 | results.close();
822 | assertTrue(count > 0);
823 |
824 | // s p ?o SELECT using a namespace prefix
825 | queryStr = "PREFIX foaf: \n" + "SELECT ?z WHERE { <" + prefix + "thor> foaf:name ?z }";
826 | query = parser.parseQuery(queryStr, baseIRI);
827 | results = sc.evaluate(query.getTupleExpr(), query.getDataset(), bindings, false);
828 | count = 0;
829 | languages = new HashSet<>();
830 | while (results.hasNext()) {
831 | count++;
832 | BindingSet set = results.next();
833 | Literal z = (Literal) set.getValue("z");
834 | assertNotNull(z);
835 | languages.add(z.getLanguage().get());
836 | }
837 | results.close();
838 | assertTrue(count > 0);
839 | assertEquals(2, languages.size());
840 | assertTrue(languages.contains("en"));
841 | assertTrue(languages.contains("is"));
842 |
843 | // ?s p o SELECT using a plain literal value with no language tag
844 | queryStr = "PREFIX rdfs: \n"
845 | + "SELECT ?s WHERE { ?s rdfs:comment \"he really knows where his towel is\" }";
846 | IRI fordUri = sail.getValueFactory().createIRI(prefix + "ford");
847 | query = parser.parseQuery(queryStr, baseIRI);
848 | results = sc.evaluate(query.getTupleExpr(), query.getDataset(), bindings, false);
849 | count = 0;
850 | while (results.hasNext()) {
851 | count++;
852 | BindingSet set = results.next();
853 | IRI s = (IRI) set.getValue("s");
854 | assertNotNull(s);
855 | assertEquals(s, fordUri);
856 | }
857 | results.close();
858 | assertTrue(count > 0);
859 |
860 | // ?s p o SELECT using a language-specific literal value
861 | queryStr = "PREFIX foaf: \n" + "SELECT ?s WHERE { ?s foaf:name \"Thor\"@en }";
862 | query = parser.parseQuery(queryStr, baseIRI);
863 | results = sc.evaluate(query.getTupleExpr(), query.getDataset(), bindings, false);
864 | count = 0;
865 | while (results.hasNext()) {
866 | count++;
867 | BindingSet set = results.next();
868 | IRI s = (IRI) set.getValue("s");
869 | assertNotNull(s);
870 | assertEquals(s, thorUri);
871 | }
872 | results.close();
873 | assertTrue(count > 0);
874 |
875 | // The language tag is necessary
876 | queryStr = "PREFIX foaf: \n" + "SELECT ?s WHERE { ?s foaf:name \"Thor\" }";
877 | query = parser.parseQuery(queryStr, baseIRI);
878 | results = sc.evaluate(query.getTupleExpr(), query.getDataset(), bindings, false);
879 | count = 0;
880 | while (results.hasNext()) {
881 | count++;
882 | results.next();
883 | }
884 | results.close();
885 | assertEquals(0, count);
886 |
887 | // ?s p o SELECT using a typed literal value
888 | queryStr = "PREFIX foaf: \n" + "PREFIX xsd: \n" + "SELECT ?s WHERE { ?s foaf:msnChatID \"Thorster123\"^^xsd:string }";
889 | query = parser.parseQuery(queryStr, baseIRI);
890 | results = sc.evaluate(query.getTupleExpr(), query.getDataset(), bindings, false);
891 | count = 0;
892 | while (results.hasNext()) {
893 | count++;
894 | BindingSet set = results.next();
895 | IRI s = (IRI) set.getValue("s");
896 | assertNotNull(s);
897 | assertEquals(s, thorUri);
898 | }
899 | results.close();
900 | assertTrue(count > 0);
901 |
902 | // In RDF 1.1, the xsd:string datatype is the default, and does not need to be given in the query
903 | queryStr = "PREFIX foaf: \n"
904 | + "PREFIX xsd: \n"
905 | + "SELECT ?s WHERE { ?s foaf:msnChatID \"Thorster123\" }";
906 | query = parser.parseQuery(queryStr, baseIRI);
907 | results = sc.evaluate(query.getTupleExpr(), query.getDataset(), bindings, false);
908 | assertEquals(1, IterUtils.count(results));
909 |
910 | // s ?p o SELECT
911 | // TODO: commented out languages for now
912 | queryStr = "PREFIX foaf: \n" + "PREFIX xsd: \n" + "SELECT ?p WHERE { <" + prefix + "thor> ?p \"Thor\"@en }";
913 | query = parser.parseQuery(queryStr, baseIRI);
914 | IRI foafNameUri = sail.getValueFactory().createIRI("http://xmlns.com/foaf/0.1/name");
915 | results = sc.evaluate(query.getTupleExpr(), query.getDataset(), bindings, false);
916 | count = 0;
917 | while (results.hasNext()) {
918 | count++;
919 | BindingSet set = results.next();
920 | IRI p = (IRI) set.getValue("p");
921 | assertNotNull(p);
922 | assertEquals(p, foafNameUri);
923 | }
924 | results.close();
925 | assertTrue(count > 0);
926 |
927 | // context-specific SELECT
928 | queryStr = "PREFIX foaf: \n" + "SELECT ?z\n" + "FROM <" + prefix + "ctx1>\n" + "WHERE { <" + prefix + "thor> foaf:name ?z }";
929 | query = parser.parseQuery(queryStr, baseIRI);
930 | results = sc.evaluate(query.getTupleExpr(), query.getDataset(), bindings, false);
931 | count = 0;
932 | languages = new HashSet<>();
933 | while (results.hasNext()) {
934 | count++;
935 | BindingSet set = results.next();
936 | Literal z = (Literal) set.getValue("z");
937 | assertNotNull(z);
938 | languages.add(z.getLanguage().get());
939 | }
940 | results.close();
941 | assertTrue(count > 0);
942 | assertEquals(2, languages.size());
943 | assertTrue(languages.contains("en"));
944 | assertTrue(languages.contains("is"));
945 | queryStr = "PREFIX foaf: \n" + "SELECT ?z\n" + "FROM \n" + "WHERE { <" + prefix + "thor> foaf:name ?z }";
946 | query = parser.parseQuery(queryStr, baseIRI);
947 | results = sc.evaluate(query.getTupleExpr(), query.getDataset(), bindings, false);
948 | count = 0;
949 | while (results.hasNext()) {
950 | count++;
951 | results.next();
952 | }
953 | results.close();
954 | assertEquals(0, count);
955 |
956 | // s p o? select without and with inferencing
957 | // TODO commented out waiting for inferencing
958 | // queryStr =
959 | // "PREFIX rdf: \n"
960 | // + "SELECT ?o\n"
961 | // + "WHERE { <" + prefix + "instance1> rdf:type ?o }";
962 | // query = parser.parseQuery(queryStr, baseIRI);
963 | // results = sc.evaluate(query.getTupleExpr(), query.getDataset(),
964 | // bindings, false);
965 | // count = 0;
966 | // while (results.hasNext()) {
967 | // count++;
968 | // BindingSet set = results.next();
969 | // IRI o = (IRI) set.getValue("o");
970 | // assertEquals(prefix + "classB", o.toString());
971 | // }
972 | // results.close();
973 | // assertEquals(1, count);
974 | // results = sc.evaluate(query.getTupleExpr(), query.getDataset(),
975 | // bindings, true);
976 | // count = 0;
977 | // boolean foundA = false, foundB = false;
978 | // while (results.hasNext()) {
979 | // count++;
980 | // BindingSet set = results.next();
981 | // IRI o = (IRI) set.getValue("o");
982 | // String s = o.toString();
983 | // if (s.equals(prefix + "classA")) {
984 | // foundA = true;
985 | // } else if (s.equals(prefix + "classB")) {
986 | // foundB = true;
987 | // }
988 | // }
989 | // results.close();
990 | // assertEquals(2, count);
991 | // assertTrue(foundA);
992 | // assertTrue(foundB);
993 |
994 | }
995 | }
996 |
997 | @Test
998 | public void testJoins() throws Exception {
999 | addTestFile();
1000 |
1001 | SPARQLParser parser = new SPARQLParser();
1002 | BindingSet bindings = new EmptyBindingSet();
1003 | String baseIRI = "http://example.org/bogus/";
1004 |
1005 | try (SailConnection sc = getConnection()) {
1006 | CloseableIteration extends BindingSet, QueryEvaluationException> results;
1007 | int count;
1008 | String queryStr = "PREFIX : \n" +
1009 | "PREFIX foaf: \n" +
1010 | "SELECT ?foaf WHERE {\n" +
1011 | " :ford foaf:knows ?friend .\n" +
1012 | " ?friend foaf:knows ?foaf .\n" +
1013 | "}";
1014 | ParsedQuery query = parser.parseQuery(queryStr, baseIRI);
1015 | results = sc.evaluate(query.getTupleExpr(), query.getDataset(), bindings, false);
1016 | count = 0;
1017 | while (results.hasNext()) {
1018 | count++;
1019 | BindingSet set = results.next();
1020 | IRI foaf = (IRI) set.getValue("foaf");
1021 | assertTrue(foaf.stringValue().startsWith("urn:com.tinkerpop.blueprints.pgm.oupls.sail.test/"));
1022 | }
1023 | results.close();
1024 | assertEquals(4, count);
1025 | }
1026 | }
1027 |
1028 | // listeners ///////////////////////////////////////////////////////////////
1029 | // (disabled for Sails which do not implement NotifyingSail)
1030 |
1031 | @Test
1032 | public void testSailConnectionListeners() throws Exception {
1033 | if (sail instanceof NotifyingSail) {
1034 | IRI uriA = sail.getValueFactory().createIRI("http://example.org/uriA");
1035 | IRI uriB = sail.getValueFactory().createIRI("http://example.org/uriB");
1036 | IRI uriC = sail.getValueFactory().createIRI("http://example.org/uriC");
1037 |
1038 | TestListener listener1 = new TestListener(), listener2 = new TestListener();
1039 | try (NotifyingSailConnection sc = (NotifyingSailConnection) getConnection()) {
1040 | sc.clear();
1041 | commit(sc);
1042 |
1043 | // Add a listener and add statements
1044 | sc.addConnectionListener(listener1);
1045 | sc.addStatement(uriA, uriB, uriC, uriA);
1046 | sc.addStatement(uriB, uriC, uriA, uriA);
1047 | commit(sc);
1048 |
1049 | // Add another listener and remove a statement
1050 | sc.addConnectionListener(listener2);
1051 | sc.removeStatements(uriA, null, null);
1052 | commit(sc);
1053 |
1054 | assertEquals(2, listener1.getAdded());
1055 | assertEquals(0, listener2.getAdded());
1056 | assertEquals(1, listener1.getRemoved());
1057 | assertEquals(1, listener2.getRemoved());
1058 |
1059 | // Remove a listener and clear
1060 | sc.removeConnectionListener(listener1);
1061 | sc.clear();
1062 | commit(sc);
1063 |
1064 | assertEquals(1, listener1.getRemoved());
1065 | assertEquals(2, listener2.getRemoved());
1066 | }
1067 | }
1068 | }
1069 |
1070 | @Test
1071 | public void testSailChangedListeners() throws Exception {
1072 | if (sail instanceof NotifyingSail) {
1073 | final Collection events = new LinkedList<>();
1074 | SailChangedListener listener = events::add;
1075 | ((NotifyingSail) sail).addSailChangedListener(listener);
1076 | IRI uriA = sail.getValueFactory().createIRI("http://example.org/uriA");
1077 | IRI uriB = sail.getValueFactory().createIRI("http://example.org/uriB");
1078 | IRI uriC = sail.getValueFactory().createIRI("http://example.org/uriC");
1079 | try (SailConnection sc = getConnection()) {
1080 | sc.clear();
1081 | commit(sc);
1082 | events.clear();
1083 | assertEquals(0, events.size());
1084 | sc.addStatement(uriA, uriB, uriC, uriA);
1085 | sc.addStatement(uriB, uriC, uriA, uriA);
1086 | // Events are buffered until the commit
1087 | assertEquals(0, events.size());
1088 | commit(sc);
1089 | // Only one SailChangedEvent per commit
1090 | assertEquals(1, events.size());
1091 | SailChangedEvent event = events.iterator().next();
1092 | assertTrue(event.statementsAdded());
1093 | assertFalse(event.statementsRemoved());
1094 | events.clear();
1095 | assertEquals(0, events.size());
1096 | sc.removeStatements(uriA, uriB, uriC, uriA);
1097 | commit(sc);
1098 | assertEquals(1, events.size());
1099 | event = events.iterator().next();
1100 | assertFalse(event.statementsAdded());
1101 | assertTrue(event.statementsRemoved());
1102 | events.clear();
1103 | assertEquals(0, events.size());
1104 | sc.clear();
1105 | commit(sc);
1106 | assertEquals(1, events.size());
1107 | event = events.iterator().next();
1108 | assertFalse(event.statementsAdded());
1109 | assertTrue(event.statementsRemoved());
1110 | }
1111 | }
1112 | }
1113 |
1114 | // namespaces //////////////////////////////////////////////////////////////
1115 |
1116 | @Test
1117 | public void testClearNamespaces() throws Exception {
1118 | addTestFile();
1119 |
1120 | try (SailConnection sc = getConnection()) {
1121 | CloseableIteration extends Namespace, SailException> namespaces;
1122 | int count;
1123 | count = 0;
1124 | namespaces = sc.getNamespaces();
1125 | while (namespaces.hasNext()) {
1126 | namespaces.next();
1127 | count++;
1128 | }
1129 | namespaces.close();
1130 | assertTrue(count > 0);
1131 | // TODO: actually clear namespaces (but this wipes them out for
1132 | // subsequent tests)
1133 | }
1134 | }
1135 |
1136 | @Test
1137 | public void testGetNamespace() throws Exception {
1138 | addTestFile();
1139 |
1140 | try (SailConnection sc = getConnection()) {
1141 | String name;
1142 | name = sc.getNamespace("bogus");
1143 | assertNull(name);
1144 | name = sc.getNamespace("rdfs");
1145 | assertEquals(name, "http://www.w3.org/2000/01/rdf-schema#");
1146 | }
1147 | }
1148 |
1149 | private void showNamespaces(final SailConnection c) throws SailException {
1150 | System.out.println("namespaces:");
1151 | try (CloseableIteration extends Namespace, SailException> iter = c.getNamespaces()) {
1152 | while (iter.hasNext()) {
1153 | Namespace n = iter.next();
1154 | System.out.println("\t" + n.getPrefix() + ":\t" + n.getName());
1155 | }
1156 | }
1157 | }
1158 |
1159 | @Test
1160 | public void testGetNamespaces() throws Exception {
1161 | try (SailConnection sc = getConnection()) {
1162 | CloseableIteration extends Namespace, SailException> namespaces;
1163 | int before = 0, during = 0, after = 0;
1164 | // just iterate through all namespaces
1165 | namespaces = sc.getNamespaces();
1166 | while (namespaces.hasNext()) {
1167 | Namespace ns = namespaces.next();
1168 | before++;
1169 | // System.out.println("namespace: " + ns);
1170 | }
1171 | namespaces.close();
1172 | // Note: assumes that these namespace prefixes are unused.
1173 | int nTests = 10;
1174 | String prefixPrefix = "testns";
1175 | String namePrefix = "http://example.org/test";
1176 | for (int i = 0; i < nTests; i++) {
1177 | sc.setNamespace(prefixPrefix + i, namePrefix + i);
1178 | }
1179 | commit(sc);
1180 | namespaces = sc.getNamespaces();
1181 | while (namespaces.hasNext()) {
1182 | Namespace ns = namespaces.next();
1183 | during++;
1184 | String prefix = ns.getPrefix();
1185 | String name = ns.getName();
1186 | if (prefix.startsWith(prefixPrefix)) {
1187 | assertEquals(name, namePrefix + prefix.substring(prefixPrefix.length()));
1188 | }
1189 | }
1190 | namespaces.close();
1191 | for (int i = 0; i < nTests; i++) {
1192 | sc.removeNamespace(prefixPrefix + i);
1193 | }
1194 | commit(sc);
1195 | namespaces = sc.getNamespaces();
1196 | while (namespaces.hasNext()) {
1197 | namespaces.next();
1198 | after++;
1199 | }
1200 | namespaces.close();
1201 | assertEquals(during, before + nTests);
1202 | assertEquals(after, before);
1203 | }
1204 | }
1205 |
1206 | @Test
1207 | public void testSetNamespace() throws Exception {
1208 | try (SailConnection sc = getConnection()) {
1209 | String prefix = "foo";
1210 | String emptyPrefix = "";
1211 | String name = "http://example.org/foo";
1212 | String otherName = "http://example.org/bar";
1213 |
1214 | sc.removeNamespace(prefix);
1215 | sc.removeNamespace(emptyPrefix);
1216 | commit(sc);
1217 |
1218 | // Namespace initially absent?
1219 | assertNull(sc.getNamespace(prefix));
1220 | assertNull(sc.getNamespace(emptyPrefix));
1221 |
1222 | // Can we set the namespace?
1223 | sc.setNamespace(prefix, name);
1224 | commit(sc);
1225 | assertEquals(sc.getNamespace(prefix), name);
1226 |
1227 | // Can we reset the namespace?
1228 | sc.setNamespace(prefix, otherName);
1229 | commit(sc);
1230 | assertEquals(sc.getNamespace(prefix), otherName);
1231 |
1232 | // Can we use an empty namespace prefix?
1233 | sc.setNamespace(emptyPrefix, name);
1234 | commit(sc);
1235 | assertEquals(sc.getNamespace(emptyPrefix), name);
1236 | }
1237 | }
1238 |
1239 | @Test
1240 | public void testRemoveNamespace() throws Exception {
1241 | try (SailConnection sc = getConnection()) {
1242 | String prefix = "foo";
1243 | String emptyPrefix = "";
1244 | String name = "http://example.org/foo";
1245 |
1246 | // Set namespace initially.
1247 | sc.setNamespace(prefix, name);
1248 | commit(sc);
1249 | assertEquals(sc.getNamespace(prefix), name);
1250 |
1251 | // Remove the namespace and make sure it's gone.
1252 | sc.removeNamespace(prefix);
1253 | commit(sc);
1254 | assertNull(sc.getNamespace(prefix));
1255 |
1256 | // Same thing for the default namespace.
1257 | sc.setNamespace(emptyPrefix, name);
1258 | commit(sc);
1259 | assertEquals(sc.getNamespace(emptyPrefix), name);
1260 | sc.removeNamespace(emptyPrefix);
1261 | commit(sc);
1262 | assertNull(sc.getNamespace(emptyPrefix));
1263 | }
1264 | }
1265 |
1266 | // connections and transactions ////////////////////////////////////////////
1267 |
1268 | @Test
1269 | public void testPersistentCommits() throws Exception {
1270 | SailConnection sc;
1271 | long count;
1272 | IRI uriA = sail.getValueFactory().createIRI("http://example.org/test/persistentCommits#a");
1273 | IRI uriB = sail.getValueFactory().createIRI("http://example.org/test/persistentCommits#b");
1274 | IRI uriC = sail.getValueFactory().createIRI("http://example.org/test/persistentCommits#c");
1275 | sc = getConnection();
1276 | try {
1277 | count = countStatements(sc, uriA, null, null, false);
1278 | assertEquals(0, count);
1279 | sc.close();
1280 |
1281 | sc = getConnection();
1282 | sc.addStatement(uriA, uriB, uriC);
1283 | count = countStatements(sc, uriA, null, null, false);
1284 | assertNotEquals(0, count);
1285 | commit(sc);
1286 | sc.close();
1287 |
1288 | sc = getConnection();
1289 | count = countStatements(sc, uriA, null, null, false);
1290 | assertNotEquals(0, count);
1291 | sc.close();
1292 |
1293 | sc = getConnection();
1294 | sc.removeStatements(uriA, null, null);
1295 | count = countStatements(sc, uriA, null, null, false);
1296 | assertEquals(0, count);
1297 | commit(sc);
1298 | sc.close();
1299 |
1300 | sc = getConnection();
1301 | count = countStatements(sc, uriA, null, null, false);
1302 | assertEquals(0, count);
1303 | } finally {
1304 | sc.close();
1305 | }
1306 | }
1307 |
1308 | @Test
1309 | public void testVisibilityOfChanges() throws Exception {
1310 | SailConnection sc1, sc2;
1311 | long count;
1312 | IRI uriA = sail.getValueFactory().createIRI(
1313 | "http://example.org/test/visibilityOfChanges#a");
1314 | sc1 = getConnection();
1315 | sc2 = getConnection();
1316 | try {
1317 | sc1.clear();
1318 | sc2.clear();
1319 |
1320 | // Statement doesn't exist for either connection.
1321 | count = countStatements(sc1, uriA, null, null);
1322 | assertEquals(0, count);
1323 | count = countStatements(sc2, uriA, null, null);
1324 | assertEquals(0, count);
1325 | // First connection adds a statement. It is visible to the first
1326 | // connection, but not to the second.
1327 | sc1.addStatement(uriA, uriA, uriA);
1328 | count = countStatements(sc1, null, null, null);
1329 | assertEquals(1, count);
1330 | count = countStatements(sc2, uriA, null, null);
1331 | assertEquals(0, count);
1332 | }
1333 | finally {
1334 | sc2.close();
1335 | sc1.close();
1336 | }
1337 | }
1338 |
1339 | @Test
1340 | public void testNullContext() throws Exception {
1341 | IRI uriA = sail.getValueFactory().createIRI("http://example.org/test/nullContext#a");
1342 | IRI uriB = sail.getValueFactory().createIRI("http://example.org/test/nullContext#b");
1343 | IRI uriC = sail.getValueFactory().createIRI("http://example.org/test/nullContext#c");
1344 |
1345 | try (SailConnection sc = getConnection()) {
1346 | long count = countStatements(sc, uriA, null, null, false);
1347 | assertEquals(0, count);
1348 | sc.addStatement(uriA, uriB, uriC);
1349 | Statement statement = sc.getStatements(uriA, uriB, uriC, false, new Resource[]{null}).next();
1350 | Resource context = statement.getContext();
1351 | assertNull(context);
1352 | sc.removeStatements(uriA, null, null);
1353 | assertFalse(sc.getStatements(uriA, uriB, uriC, false, new Resource[]{null}).hasNext());
1354 | }
1355 | }
1356 |
1357 | // inference ////////////////////////////////////////////////////////////////
1358 |
1359 | @Ignore
1360 | @Test
1361 | public void testInference() throws Exception {
1362 | if (null != inferencer) {
1363 | IRI uriA = sail.getValueFactory().createIRI("http://example.org/uriA");
1364 | IRI uriB = sail.getValueFactory().createIRI("http://example.org/uriB");
1365 | IRI classX = sail.getValueFactory().createIRI("http://example.org/classX");
1366 | IRI classY = sail.getValueFactory().createIRI("http://example.org/classY");
1367 |
1368 | try (SailConnection sc = inferencer.getConnection()) {
1369 | sc.begin();
1370 | sc.clear();
1371 |
1372 | sc.addStatement(classX, RDFS.SUBCLASSOF, classY);
1373 | sc.addStatement(uriA, RDF.TYPE, classX);
1374 | sc.addStatement(uriB, RDF.TYPE, classY);
1375 | commit(sc);
1376 |
1377 | //showStatements(sc, uriA, RDF.TYPE, null);
1378 |
1379 | assertEquals(3, countStatements(sc, uriA, RDF.TYPE, null, true));
1380 | assertEquals(1, countStatements(sc, uriA, RDF.TYPE, null, false));
1381 | assertEquals(2, countStatements(sc, uriB, RDF.TYPE, null, true));
1382 | assertEquals(1, countStatements(sc, uriB, RDF.TYPE, null, false));
1383 |
1384 | if (uniqueStatements) {
1385 | sc.addStatement(uriA, RDF.TYPE, classY);
1386 | commit(sc);
1387 |
1388 | //showStatements(sc, uriA, RDF.TYPE, null);
1389 | assertEquals(3, countStatements(sc, uriA, RDF.TYPE, null, true));
1390 | assertEquals(2, countStatements(sc, uriA, RDF.TYPE, null, false));
1391 |
1392 | sc.removeStatements(uriA, RDF.TYPE, classY);
1393 | commit(sc);
1394 |
1395 | assertEquals(3, countStatements(sc, uriA, RDF.TYPE, null, true));
1396 | assertEquals(2, countStatements(sc, uriA, RDF.TYPE, null, false));
1397 |
1398 | //sc.removeStatements(uriA, RDF.TYPE, classX);
1399 | //commit(sc);
1400 | //assertEquals(1, countStatements(sc, uriA, RDF.TYPE, null));
1401 | }
1402 | }
1403 | }
1404 | }
1405 |
1406 | // TODO: concurrency testing ///////////////////////////////////////////////
1407 | // //////////////////////////////////////////////////////////////////////////
1408 |
1409 | private class TestListener implements SailConnectionListener {
1410 | private int added = 0, removed = 0;
1411 |
1412 | public void statementAdded(final Statement statement) {
1413 | added++;
1414 | }
1415 |
1416 | public void statementRemoved(final Statement statement) {
1417 | removed++;
1418 | }
1419 |
1420 | public int getAdded() {
1421 | return added;
1422 | }
1423 |
1424 | public int getRemoved() {
1425 | return removed;
1426 | }
1427 | }
1428 |
1429 | protected void showStatements(final SailConnection sc,
1430 | final Resource subject,
1431 | final IRI predicate,
1432 | final Value object,
1433 | final Resource... contexts) throws SailException {
1434 | int count = 0;
1435 | try (CloseableIteration, SailException> statements
1436 | = sc.getStatements(subject, predicate, object, true, contexts)) {
1437 | while (statements.hasNext()) {
1438 | System.out.println("" + count + ") " + statements.next());
1439 | count++;
1440 | }
1441 | }
1442 | }
1443 |
1444 | protected long countStatements(final SailConnection sc,
1445 | final Resource subject,
1446 | final IRI predicate,
1447 | final Value object,
1448 | final Resource... contexts) throws SailException {
1449 | return countStatements(sc, subject, predicate, object, false, contexts);
1450 | }
1451 |
1452 | protected long countStatements(final SailConnection sc,
1453 | final Resource subject,
1454 | final IRI predicate,
1455 | final Value object,
1456 | final boolean includeInferred,
1457 | final Resource... contexts) throws SailException {
1458 | return IterUtils.count(sc.getStatements(subject, predicate, object, includeInferred, contexts));
1459 | }
1460 |
1461 | private Set toSet(final CloseableIteration extends Statement, SailException> i) throws SailException {
1462 | try {
1463 | Set set = new HashSet<>();
1464 | while (i.hasNext()) {
1465 | set.add(i.next());
1466 | }
1467 | return set;
1468 | } finally {
1469 | i.close();
1470 | }
1471 | }
1472 |
1473 | private void addFile(final InputStream in,
1474 | final RDFFormat format) throws Exception {
1475 | Preconditions.checkNotNull(in);
1476 |
1477 | try {
1478 | try (SailConnection sc = getConnection()) {
1479 | RDFHandler h = new SailAdder(sc);
1480 | RDFParser p = Rio.createParser(format);
1481 | p.setRDFHandler(h);
1482 | p.parse(in, "http://example.org/bogusBaseIRI/");
1483 | commit(sc);
1484 | }
1485 | } finally {
1486 | in.close();
1487 | }
1488 | }
1489 |
1490 | private SailConnection getConnection() {
1491 | SailConnection sc = sail.getConnection();
1492 | sc.begin();
1493 | return sc;
1494 | }
1495 |
1496 | private void commit(final SailConnection conn) {
1497 | conn.commit();
1498 | conn.begin();
1499 | }
1500 |
1501 | protected void addTestFile() throws Exception {
1502 | addFile(SailTest.class.getResourceAsStream("graph-example-sail-test.trig"), RDFFormat.TRIG);
1503 | }
1504 | }
1505 |
--------------------------------------------------------------------------------
/src/test/resources/net/fortytwo/tpop/sail/graph-example-sail-test.trig:
--------------------------------------------------------------------------------
1 | @prefix foaf: .
2 | @prefix rdfs: .
3 | @prefix xsd: .
4 | @prefix : .
5 |
6 | {
7 | :arthur a foaf:Person;
8 | foaf:knows :ford, :zaphod, :thor ;
9 | rdfs:comment "he's a jerk" .
10 | }
11 |
12 | :ctx1 {
13 | :ford a foaf:Person;
14 | foaf:knows :arthur, :zaphod;
15 | rdfs:comment "he really knows where his towel is".
16 |
17 | :zaphod a foaf:Person;
18 | foaf:knows :ford;
19 | rdfs:comment "so cool you could keep a side of meat in him for a month".
20 |
21 | :thor a foaf:God;
22 | foaf:name "Thor"@en;
23 | foaf:name "\u00DE\u00F3r"@is;
24 | foaf:msnChatID "Thorster123"^^xsd:string.
25 |
26 | :stringValue :hasValue "foo"^^xsd:string.
27 | :byteValue :hasValue "99"^^xsd:byte.
28 | :booleanValue :hasValue "false"^^xsd:boolean.
29 | :intValue :hasValue "42"^^xsd:int.
30 | :shortValue :hasValue "42"^^xsd:short.
31 | :longValue :hasValue "42"^^xsd:long.
32 | :floatValue :hasValue "3.1415926"^^xsd:float.
33 | :doubleValue :hasValue "3.1415926"^^xsd:double.
34 | :dateTimeValue :hasValue "2002-10-10T12:00:00-05:00"^^xsd:dateTime.
35 |
36 | :classA a rdfs:Class.
37 |
38 | :classB a rdfs:Class;
39 | rdfs:subClassOf :classA.
40 |
41 | :instance1 a :classB.
42 | }
43 |
--------------------------------------------------------------------------------