1 /**
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18 package org.apache.commons.rdf.rdf4j;
19
20 import java.util.Arrays;
21 import java.util.EnumSet;
22 import java.util.Objects;
23 import java.util.Set;
24 import java.util.UUID;
25
26 // To avoid confusion, avoid importing
27 // classes that are in both
28 // commons.rdf and openrdf.model (e.g. IRI, Literal)
29 import org.apache.commons.rdf.api.BlankNode;
30 import org.apache.commons.rdf.api.BlankNodeOrIRI;
31 import org.apache.commons.rdf.api.Dataset;
32 import org.apache.commons.rdf.api.Graph;
33 import org.apache.commons.rdf.api.Quad;
34 import org.apache.commons.rdf.api.RDFTerm;
35 import org.apache.commons.rdf.api.RDF;
36 import org.apache.commons.rdf.api.Triple;
37 import org.apache.commons.rdf.api.TripleLike;
38 import org.apache.commons.rdf.rdf4j.impl.InternalRDF4JFactory;
39 import org.eclipse.rdf4j.model.BNode;
40 import org.eclipse.rdf4j.model.IRI;
41 import org.eclipse.rdf4j.model.Literal;
42 import org.eclipse.rdf4j.model.Model;
43 import org.eclipse.rdf4j.model.Resource;
44 import org.eclipse.rdf4j.model.Statement;
45 import org.eclipse.rdf4j.model.Value;
46 import org.eclipse.rdf4j.model.ValueFactory;
47 import org.eclipse.rdf4j.model.impl.LinkedHashModel;
48 import org.eclipse.rdf4j.model.impl.SimpleValueFactory;
49 import org.eclipse.rdf4j.repository.Repository;
50 import org.eclipse.rdf4j.repository.RepositoryConnection;
51 import org.eclipse.rdf4j.repository.sail.SailRepository;
52 import org.eclipse.rdf4j.sail.Sail;
53 import org.eclipse.rdf4j.sail.memory.MemoryStore;
54
55 /**
56 * RDF4J implementation of RDF.
57 * <p>
58 * The {@link #RDF4J()} constructor uses a {@link SimpleValueFactory} to create
59 * corresponding RDF4J {@link Value} instances. Alternatively, this factory can
60 * be constructed with a different {@link ValueFactory} using
61 * {@link #RDF4J(ValueFactory)}.
62 * <p>
63 * {@link #asRDFTerm(Value)} can be used to convert any RDF4J {@link Value} to
64 * an RDFTerm. Note that adapted {@link BNode}s are considered equal if they are
65 * converted with the same {@link RDF4J} instance and have the same
66 * {@link BNode#getID()}.
67 * <p>
68 * {@link #createGraph()} creates a new Graph backed by {@link LinkedHashModel}.
69 * To use other models, see {@link #asGraph(Model)}.
70 * <p>
71 * To adapt a RDF4J {@link Repository} as a {@link Dataset} or {@link Graph},
72 * use {@link #asDataset(Repository, Option...)} or
73 * {@link #asGraph(Repository, Option...)}.
74 * <p>
75 * {@link #asTriple(Statement)} can be used to convert a RDF4J {@link Statement}
76 * to a Commons RDF {@link Triple}, and equivalent {@link #asQuad(Statement)} to
77 * convert a {@link Quad}.
78 * <p>
79 * To convert any {@link Triple} or {@link Quad} to to RDF4J {@link Statement},
80 * use {@link #asStatement(TripleLike)}. This recognises previously converted
81 * {@link RDF4JTriple}s and {@link RDF4JQuad}s without re-converting their
82 * {@link RDF4JTripleLike#asStatement()}.
83 * <p>
84 * Likewise, {@link #asValue(RDFTerm)} can be used to convert any Commons RDF
85 * {@link RDFTerm} to a corresponding RDF4J {@link Value}. This recognises
86 * previously converted {@link RDF4JTerm}s without re-converting their
87 * {@link RDF4JTerm#asValue()}.
88 * <p>
89 * For the purpose of {@link BlankNode} equivalence, this factory contains an
90 * internal {@link UUID} salt that is used by adapter methods like
91 * {@link #asQuad(Statement)}, {@link #asTriple(Statement)},
92 * {@link #asRDFTerm(Value)} as well as {@link #createBlankNode(String)}. As
93 * RDF4J {@link BNode} instances from multiple repositories or models may have
94 * the same {@link BNode#getID()}, converting them with the above methods might
95 * cause accidental {@link BlankNode} equivalence. Note that the {@link Graph}
96 * and {@link Dataset} adapter methods like
97 * {@link #asDataset(Repository, Option...)} and
98 * {@link #asGraph(Repository, Option...)} therefore uses a unique {@link RDF4J}
99 * internally.
100 *
101 * @see RDF
102 *
103 */
104 @SuppressWarnings("PMD.UnnecessaryFullyQualifiedName") // we use fully-qualified names for clarity
105 public final class RDF4J implements RDF {
106
107 /**
108 * InternalRDF4JFactory is deliberately abstract
109 */
110 private static InternalRDF4JFactory rdf4j = new InternalRDF4JFactory() {
111 };
112
113 public enum Option {
114 /**
115 * The Graph/Dataset should include any inferred statements
116 */
117 includeInferred,
118 /**
119 * The graph/dataset should handle {@link Repository#initialize()} (if
120 * needed) and {@link Repository#shutDown()} on {@link Graph#close()} /
121 * {@link Dataset#close()}.
122 */
123 handleInitAndShutdown
124 }
125
126 private final UUID salt;
127
128 private final ValueFactory valueFactory;
129
130 /**
131 * Construct an {@link RDF4J}.
132 *
133 */
134 public RDF4J() {
135 this(SimpleValueFactory.getInstance(), UUID.randomUUID());
136 }
137
138 /**
139 * Construct an {@link RDF4J}.
140 * <p>
141 * This constructor is intended for use with the value factory from
142 * {@link Repository#getValueFactory()} when using Repository-based graphs
143 * and datasets.
144 *
145 * @param valueFactory
146 * The RDF4J {@link ValueFactory} to use
147 */
148 public RDF4J(final ValueFactory valueFactory) {
149 this(valueFactory, UUID.randomUUID());
150 }
151
152 /**
153 * Construct an {@link RDF4J}.
154 * <p>
155 * This constructor may be used if reproducible
156 * {@link BlankNode#uniqueReference()} in {@link BlankNode} is desirable.
157 *
158 * @param salt
159 * An {@link UUID} salt to be used by any created
160 * {@link BlankNode}s for the purpose of
161 * {@link BlankNode#uniqueReference()}
162 */
163 public RDF4J(final UUID salt) {
164 this(SimpleValueFactory.getInstance(), salt);
165 }
166
167 /**
168 * Construct an {@link RDF4J}.
169 * <p>
170 * This constructor may be used if reproducible
171 * {@link BlankNode#uniqueReference()} in {@link BlankNode} is desirable.
172 *
173 * @param valueFactory
174 * The RDF4J {@link ValueFactory} to use
175 * @param salt
176 * An {@link UUID} salt to be used by any created
177 * {@link BlankNode}s for the purpose of
178 * {@link BlankNode#uniqueReference()}
179 */
180 public RDF4J(final ValueFactory valueFactory, final UUID salt) {
181 this.valueFactory = valueFactory;
182 this.salt = salt;
183 }
184
185 /**
186 * Adapt a RDF4J {@link Statement} as a Commons RDF {@link Quad}.
187 * <p>
188 * For the purpose of {@link BlankNode} equivalence, this method will use an
189 * internal salt UUID that is unique per instance of {@link RDF4J}.
190 * <p>
191 * <strong>NOTE:</strong> If combining RDF4J {@link Statement}s multiple
192 * repositories or models, then their {@link BNode}s may have the same
193 * {@link BNode#getID()}, which with this method would become equivalent
194 * according to {@link BlankNode#equals(Object)} and
195 * {@link BlankNode#uniqueReference()}, unless a separate {@link RDF4J}
196 * instance is used per RDF4J repository/model.
197 *
198 * @param statement
199 * The statement to convert
200 * @return A {@link RDF4JQuad} that is equivalent to the statement
201 */
202 public RDF4JQuad asQuad(final Statement statement) {
203 return rdf4j.createQuadImpl(statement, salt);
204 }
205
206 /**
207 *
208 * Adapt a RDF4J {@link Value} as a Commons RDF {@link RDFTerm}.
209 * <p>
210 * The value will be of the same kind as the term, e.g. a
211 * {@link org.eclipse.rdf4j.model.BNode} is converted to a
212 * {@link org.apache.commons.rdf.api.BlankNode}, a
213 * {@link org.eclipse.rdf4j.model.IRI} is converted to a
214 * {@link org.apache.commons.rdf.api.IRI} and a
215 * {@link org.eclipse.rdf4j.model.Literal}. is converted to a
216 * {@link org.apache.commons.rdf.api.Literal}
217 * <p>
218 * For the purpose of {@link BlankNode} equivalence, this method will use an
219 * internal salt UUID that is unique per instance of {@link RDF4J}.
220 * <p>
221 * <strong>NOTE:</strong> If combining RDF4J values from multiple
222 * repositories or models, then their {@link BNode}s may have the same
223 * {@link BNode#getID()}, which with this method would become equivalent
224 * according to {@link BlankNode#equals(Object)} and
225 * {@link BlankNode#uniqueReference()}, unless a separate {@link RDF4J}
226 * instance is used per RDF4J repository/model.
227 *
228 * @param value
229 * The RDF4J {@link Value} to convert.
230 * @return A {@link RDFTerm} that corresponds to the RDF4J value
231 * @throws IllegalArgumentException
232 * if the value is not a BNode, Literal or IRI
233 */
234 public RDF4JTerm asRDFTerm(final Value value) {
235 return asRDFTerm(value, salt);
236 }
237
238 /**
239 *
240 * Adapt a RDF4J
241 * {@link org.eclipse.rdf4j.model.BNode} as a Commons RDF
242 * {@link org.apache.commons.rdf.api.BlankNode}
243 * <p>
244 * For the purpose of {@link BlankNode} equivalence, this method will use an
245 * internal salt UUID that is unique per instance of {@link RDF4J}.
246 * <p>
247 * <strong>NOTE:</strong> If combining RDF4J values from multiple
248 * repositories or models, then their {@link BNode}s may have the same
249 * {@link BNode#getID()}, which with this method would become equivalent
250 * according to {@link BlankNode#equals(Object)} and
251 * {@link BlankNode#uniqueReference()}, unless a separate {@link RDF4J}
252 * instance is used per RDF4J repository/model.
253 *
254 * @param value
255 * The RDF4J {@link BNode} to convert.
256 * @return A {@link RDF4JBlankNode} that corresponds to the RDF4J BNode
257 */
258 public RDF4JBlankNode asRDFTerm(final BNode value) {
259 return rdf4j.createBlankNodeImpl(value, salt);
260 }
261
262 /**
263 *
264 * Adapt a RDF4J
265 * {@link org.eclipse.rdf4j.model.Literal} as a Commons RDF
266 * {@link org.apache.commons.rdf.api.Literal}
267 * <p>
268 * @param value
269 * The RDF4J {@link Literal} to convert.
270 * @return A {@link RDF4JLiteral} that corresponds to the RDF4J literal
271 */
272 public RDF4JLiteral asRDFTerm(final Literal value) {
273 return rdf4j.createLiteralImpl(value);
274 }
275
276 /**
277 *
278 * Adapt a RDF4J
279 * {@link org.eclipse.rdf4j.model.IRI} as a Commons RDF
280 * {@link org.apache.commons.rdf.api.IRI}
281 * <p>
282 * @param value
283 * The RDF4J {@link Value} to convert.
284 * @return A {@link RDF4JIRI} that corresponds to the RDF4J IRI
285 */
286 public RDF4JIRI asRDFTerm(final org.eclipse.rdf4j.model.IRI value) {
287 return rdf4j.createIRIImpl(value);
288 }
289
290 /**
291 *
292 * Adapt a RDF4J
293 * {@link org.eclipse.rdf4j.model.Resource} as a Commons RDF
294 * {@link org.apache.commons.rdf.api.BlankNodeOrIRI}
295 * <p>
296 * @param value
297 * The RDF4J {@link Value} to convert.
298 * @return A {@link RDF4JBlankNodeOrIRI} that corresponds to the RDF4J Resource
299 */
300 public RDF4JBlankNodeOrIRI asRDFTerm(final org.eclipse.rdf4j.model.Resource value) {
301 if(value instanceof IRI){
302 return asRDFTerm((IRI)value);
303 } else if (value instanceof BNode){
304 return asRDFTerm((BNode)value);
305 }
306 throw new IllegalArgumentException("Value is not a BNode or IRI: " + value.getClass());
307 }
308
309 /**
310 * Adapt a RDF4J {@link Value} as a Commons RDF {@link RDFTerm}.
311 * <p>
312 * The value will be of the same kind as the term, e.g. a
313 * {@link org.eclipse.rdf4j.model.BNode} is converted to a
314 * {@link org.apache.commons.rdf.api.BlankNode}, a
315 * {@link org.eclipse.rdf4j.model.IRI} is converted to a
316 * {@link org.apache.commons.rdf.api.IRI} and a
317 * {@link org.eclipse.rdf4j.model.Literal}. is converted to a
318 * {@link org.apache.commons.rdf.api.Literal}
319 *
320 * @param value
321 * The RDF4J {@link Value} to convert.
322 * @param salt
323 * A {@link UUID} salt to use for uniquely mapping any
324 * {@link BNode}s. The salt should typically be the same for
325 * multiple statements in the same {@link Repository} or
326 * {@link Model} to ensure {@link BlankNode#equals(Object)} and
327 * {@link BlankNode#uniqueReference()} works as intended.
328 * @return A {@link RDFTerm} that corresponds to the RDF4J value
329 * @throws IllegalArgumentException
330 * if the value is not a BNode, Literal or IRI
331 */
332 public static RDF4JTerm asRDFTerm(final Value value, final UUID salt) {
333 if (value instanceof BNode) {
334 return rdf4j.createBlankNodeImpl((BNode) value, salt);
335 }
336 if (value instanceof org.eclipse.rdf4j.model.Literal) {
337 return rdf4j.createLiteralImpl((org.eclipse.rdf4j.model.Literal) value);
338 }
339 if (value instanceof org.eclipse.rdf4j.model.IRI) {
340 return rdf4j.createIRIImpl((org.eclipse.rdf4j.model.IRI) value);
341 }
342 throw new IllegalArgumentException("Value is not a BNode, Literal or IRI: " + value.getClass());
343 }
344
345 /**
346 * Adapt an RDF4J {@link Repository} as a Commons RDF {@link Dataset}.
347 * <p>
348 * Changes to the dataset are reflected in the repository, and vice versa.
349 * <p>
350 * <strong>Note:</strong> Some operations on the {@link RDF4JDataset}
351 * requires the use of try-with-resources to close underlying
352 * {@link RepositoryConnection}s, including {@link RDF4JDataset#iterate()},
353 * {@link RDF4JDataset#stream()} and {@link RDF4JDataset#getGraphNames()}.
354 *
355 * @param repository
356 * RDF4J {@link Repository} to connect to.
357 * @param options
358 * Zero or more {@link Option}
359 * @return A {@link Dataset} backed by the RDF4J repository.
360 */
361 public RDF4JDataset asDataset(final Repository repository, final Option... options) {
362 final EnumSet<Option> opts = optionSet(options);
363 return rdf4j.createRepositoryDatasetImpl(repository, opts.contains(Option.handleInitAndShutdown),
364 opts.contains(Option.includeInferred));
365 }
366
367 /**
368 * Adapt an RDF4J {@link Model} as a Commons RDF {@link Graph}.
369 * <p>
370 * Changes to the graph are reflected in the model, and vice versa.
371 *
372 * @param model
373 * RDF4J {@link Model} to adapt.
374 * @return Adapted {@link Graph}.
375 */
376 public RDF4JGraph asGraph(final Model model) {
377 return rdf4j.createModelGraphImpl(model, this);
378 }
379
380 /**
381 * Adapt an RDF4J {@link Repository} as a Commons RDF {@link Graph}.
382 * <p>
383 * The graph will only include triples in the default graph (equivalent to
384 * context <code>new Resource[0]{null})</code> in RDF4J).
385 * <p>
386 * Changes to the graph are reflected in the repository, and vice versa.
387 * <p>
388 * <strong>Note:</strong> Some operations on the {@link RDF4JGraph} requires
389 * the use of try-with-resources to close underlying
390 * {@link RepositoryConnection}s, including {@link RDF4JGraph#iterate()} and
391 * {@link RDF4JGraph#stream()}.
392 *
393 * @param repository
394 * RDF4J {@link Repository} to connect to.
395 * @param options
396 * Zero or more {@link Option}
397 * @return A {@link Graph} backed by the RDF4J repository.
398 */
399 public RDF4JGraph asGraph(final Repository repository, final Option... options) {
400 final EnumSet<Option> opts = optionSet(options);
401 return rdf4j.createRepositoryGraphImpl(repository, opts.contains(Option.handleInitAndShutdown),
402 opts.contains(Option.includeInferred), new Resource[] { null }); // default
403 // graph
404 }
405
406 /**
407 * Adapt an RDF4J {@link Repository} as a Commons RDF {@link Graph}.
408 * <p>
409 * The graph will include triples in any contexts (e.g. the union graph).
410 * <p>
411 * Changes to the graph are reflected in the repository, and vice versa.
412 *
413 * @param repository
414 * RDF4J {@link Repository} to connect to.
415 * @param options
416 * Zero or more {@link Option}
417 * @return A union {@link Graph} backed by the RDF4J repository.
418 */
419 public RDF4JGraph asGraphUnion(final Repository repository, final Option... options) {
420 final EnumSet<Option> opts = optionSet(options);
421 return rdf4j.createRepositoryGraphImpl(repository, opts.contains(Option.handleInitAndShutdown),
422 opts.contains(Option.includeInferred), new Resource[] {}); // union
423 // graph
424
425 }
426
427 /**
428 * Adapt an RDF4J {@link Repository} as a Commons RDF {@link Graph}.
429 * <p>
430 * The graph will include triples in the specified contexts.
431 * <p>
432 * Changes to the graph are reflected in the repository, and vice versa.
433 * Triples added/removed to the graph are reflected in all the specified
434 * contexts.
435 * <p>
436 * <strong>Note:</strong> Some operations on the {@link RDF4JGraph} requires
437 * the use of try-with-resources to close underlying
438 * {@link RepositoryConnection}s, including {@link RDF4JGraph#iterate()} and
439 * {@link RDF4JGraph#stream()}.
440 *
441 * @param repository
442 * RDF4J {@link Repository} to connect to.
443 * @param contexts
444 * A {@link Set} of {@link BlankNodeOrIRI} specifying the graph
445 * names to use as a context. The set may include the value
446 * <code>null</code> to indicate the default graph. The empty set
447 * indicates any context, e.g. the <em>union graph</em>.
448 * @param option
449 * Zero or more {@link Option}s
450 * @return A {@link Graph} backed by the RDF4J repository.
451 */
452 public RDF4JGraph asGraph(final Repository repository, final Set<? extends BlankNodeOrIRI> contexts, final Option... option) {
453 final EnumSet<Option> opts = optionSet(option);
454 /** NOTE: asValue() deliberately CAN handle <code>null</code> */
455 final Resource[] resources = contexts.stream().map(g -> (Resource) asValue(g)).toArray(Resource[]::new);
456 return rdf4j.createRepositoryGraphImpl(Objects.requireNonNull(repository),
457 opts.contains(Option.handleInitAndShutdown), opts.contains(Option.includeInferred), resources);
458 }
459
460 /**
461 * Adapt a Commons RDF {@link Triple} or {@link Quad} as a RDF4J
462 * {@link Statement}.
463 * <p>
464 * If the <code>tripleLike</code> argument is an {@link RDF4JTriple} or a
465 * {@link RDF4JQuad}, then its {@link RDF4JTripleLike#asStatement()} is
466 * returned as-is. Note that this means that a {@link RDF4JTriple} would
467 * preserve its {@link Statement#getContext()}, and that any
468 * {@link BlankNode}s would be deemed equivalent in RDF4J if they have the
469 * same {@link BNode#getID()}.
470 *
471 * @param tripleLike
472 * A {@link Triple} or {@link Quad} to adapt
473 * @return A corresponding {@link Statement}
474 */
475 public Statement asStatement(final TripleLike tripleLike) {
476 if (tripleLike instanceof RDF4JTripleLike) {
477 // Return original statement - this covers both RDF4JQuad and
478 // RDF4JTriple
479 return ((RDF4JTripleLike) tripleLike).asStatement();
480 }
481
482 final org.eclipse.rdf4j.model.Resource subject = (org.eclipse.rdf4j.model.Resource) asValue(tripleLike.getSubject());
483 final org.eclipse.rdf4j.model.IRI predicate = (org.eclipse.rdf4j.model.IRI) asValue(tripleLike.getPredicate());
484 final Value object = asValue(tripleLike.getObject());
485
486 org.eclipse.rdf4j.model.Resource context = null;
487 if (tripleLike instanceof Quad) {
488 final Quad quad = (Quad) tripleLike;
489 context = (org.eclipse.rdf4j.model.Resource) asValue(quad.getGraphName().orElse(null));
490 }
491
492 return getValueFactory().createStatement(subject, predicate, object, context);
493 }
494
495 /**
496 * Adapt a RDF4J {@link Statement} as a Commons RDF {@link Triple}.
497 * <p>
498 * For the purpose of {@link BlankNode} equivalence, this method will use an
499 * internal salt UUID that is unique per instance of {@link RDF4J}.
500 * <p>
501 * <strong>NOTE:</strong> If combining RDF4J statements from multiple
502 * repositories or models, then their {@link BNode}s may have the same
503 * {@link BNode#getID()}, which with this method would become equivalent
504 * according to {@link BlankNode#equals(Object)} and
505 * {@link BlankNode#uniqueReference()}, unless a separate {@link RDF4J}
506 * instance is used per RDF4J repository/model.
507 *
508 * @param statement
509 * The RDF4J {@link Statement} to adapt.
510 * @return A {@link RDF4JTriple} that is equivalent to the statement
511 */
512 public RDF4JTriple asTriple(final Statement statement) {
513 return rdf4j.createTripleImpl(statement, salt);
514 }
515
516 /**
517 * Adapt a Commons RDF {@link RDFTerm} as a RDF4J {@link Value}.
518 * <p>
519 * The value will be of the same kind as the term, e.g. a
520 * {@link org.apache.commons.rdf.api.BlankNode} is converted to a
521 * {@link org.eclipse.rdf4j.model.BNode}, a
522 * {@link org.apache.commons.rdf.api.IRI} is converted to a
523 * {@link org.eclipse.rdf4j.model.IRI} and a
524 * {@link org.apache.commons.rdf.api.Literal} is converted to a
525 * {@link org.eclipse.rdf4j.model.Literal}.
526 * <p>
527 * If the provided {@link RDFTerm} is <code>null</code>, then the returned
528 * value is <code>null</code>.
529 * <p>
530 * If the provided term is an instance of {@link RDF4JTerm}, then the
531 * {@link RDF4JTerm#asValue()} is returned without any conversion. Note that
532 * this could mean that a {@link Value} from a different kind of
533 * {@link ValueFactory} could be returned.
534 *
535 * @param term
536 * RDFTerm to adapt to RDF4J Value
537 * @return Adapted RDF4J {@link Value}
538 */
539 public Value asValue(final RDFTerm term) {
540 if (term == null) {
541 return null;
542 }
543 if (term instanceof RDF4JTerm) {
544 // One of our own - avoid converting again.
545 // (This is crucial to avoid double-escaping in BlankNode)
546 return ((RDF4JTerm) term).asValue();
547 }
548 if (term instanceof org.apache.commons.rdf.api.IRI) {
549 final org.apache.commons.rdf.api.IRI iri = (org.apache.commons.rdf.api.IRI) term;
550 return getValueFactory().createIRI(iri.getIRIString());
551 }
552 if (term instanceof org.apache.commons.rdf.api.Literal) {
553 final org.apache.commons.rdf.api.Literal literal = (org.apache.commons.rdf.api.Literal) term;
554 final String label = literal.getLexicalForm();
555 if (literal.getLanguageTag().isPresent()) {
556 final String lang = literal.getLanguageTag().get();
557 return getValueFactory().createLiteral(label, lang);
558 }
559 final org.eclipse.rdf4j.model.IRI dataType = (org.eclipse.rdf4j.model.IRI) asValue(literal.getDatatype());
560 return getValueFactory().createLiteral(label, dataType);
561 }
562 if (term instanceof BlankNode) {
563 // This is where it gets tricky to support round trips!
564 final BlankNode blankNode = (BlankNode) term;
565 // FIXME: The uniqueReference might not be a valid BlankNode
566 // identifier..
567 // does it have to be in RDF4J?
568 return getValueFactory().createBNode(blankNode.uniqueReference());
569 }
570 throw new IllegalArgumentException("RDFTerm was not an IRI, Literal or BlankNode: " + term.getClass());
571 }
572
573 @Override
574 public RDF4JBlankNode createBlankNode() {
575 final BNode bnode = getValueFactory().createBNode();
576 return asRDFTerm(bnode);
577 }
578
579 @Override
580 public RDF4JBlankNode createBlankNode(final String name) {
581 final BNode bnode = getValueFactory().createBNode(name);
582 return asRDFTerm(bnode);
583 }
584
585 /**
586 * {@inheritDoc}
587 * <p>
588 * <strong>Note:</strong> Some operations on the {@link RDF4JDataset}
589 * requires the use of try-with-resources to close underlying
590 * {@link RepositoryConnection}s, including {@link RDF4JDataset#iterate()},
591 * {@link RDF4JDataset#stream()} and {@link RDF4JDataset#getGraphNames()}.
592 *
593 */
594 @Override
595 public RDF4JDataset createDataset() {
596 final Sail sail = new MemoryStore();
597 final Repository repository = new SailRepository(sail);
598 return rdf4j.createRepositoryDatasetImpl(repository, true, false);
599 }
600
601 @Override
602 public RDF4JGraph createGraph() {
603 return asGraph(new LinkedHashModel());
604 }
605
606 @Override
607 public RDF4JIRI createIRI(final String iri) throws IllegalArgumentException {
608 return asRDFTerm(getValueFactory().createIRI(iri));
609 }
610
611 @Override
612 public RDF4JLiteral createLiteral(final String lexicalForm) throws IllegalArgumentException {
613 final org.eclipse.rdf4j.model.Literal lit = getValueFactory().createLiteral(lexicalForm);
614 return asRDFTerm(lit);
615 }
616
617 @Override
618 public org.apache.commons.rdf.api.Literal createLiteral(final String lexicalForm, final org.apache.commons.rdf.api.IRI dataType)
619 throws IllegalArgumentException {
620 final org.eclipse.rdf4j.model.IRI iri = getValueFactory().createIRI(dataType.getIRIString());
621 final org.eclipse.rdf4j.model.Literal lit = getValueFactory().createLiteral(lexicalForm, iri);
622 return asRDFTerm(lit);
623 }
624
625 @Override
626 public org.apache.commons.rdf.api.Literal createLiteral(final String lexicalForm, final String languageTag)
627 throws IllegalArgumentException {
628 final org.eclipse.rdf4j.model.Literal lit = getValueFactory().createLiteral(lexicalForm, languageTag);
629 return asRDFTerm(lit);
630 }
631
632 @Override
633 public RDF4JTriple createTriple(final BlankNodeOrIRI subject, final org.apache.commons.rdf.api.IRI predicate, final RDFTerm object)
634 throws IllegalArgumentException {
635 final Statement statement = getValueFactory().createStatement(
636 (org.eclipse.rdf4j.model.Resource) asValue(subject), (org.eclipse.rdf4j.model.IRI) asValue(predicate),
637 asValue(object));
638 return asTriple(statement);
639 }
640
641 @Override
642 public Quad createQuad(final BlankNodeOrIRI graphName, final BlankNodeOrIRI subject, final org.apache.commons.rdf.api.IRI predicate,
643 final RDFTerm object) throws IllegalArgumentException {
644 final Statement statement = getValueFactory().createStatement(
645 (org.eclipse.rdf4j.model.Resource) asValue(subject), (org.eclipse.rdf4j.model.IRI) asValue(predicate),
646 asValue(object), (org.eclipse.rdf4j.model.Resource) asValue(graphName));
647 return asQuad(statement);
648 }
649
650 public ValueFactory getValueFactory() {
651 return valueFactory;
652 }
653
654 private EnumSet<Option> optionSet(final Option... options) {
655 final EnumSet<Option> opts = EnumSet.noneOf(Option.class);
656 opts.addAll(Arrays.asList(options));
657 return opts;
658 }
659
660 }