001/** 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.commons.rdf.jena; 019 020import java.util.Optional; 021import java.util.UUID; 022import java.util.function.Consumer; 023 024import org.apache.commons.rdf.api.BlankNode; 025import org.apache.commons.rdf.api.BlankNodeOrIRI; 026import org.apache.commons.rdf.api.Dataset; 027import org.apache.commons.rdf.api.Graph; 028import org.apache.commons.rdf.api.IRI; 029import org.apache.commons.rdf.api.Literal; 030import org.apache.commons.rdf.api.Quad; 031import org.apache.commons.rdf.api.QuadLike; 032import org.apache.commons.rdf.api.RDFSyntax; 033import org.apache.commons.rdf.api.RDFTerm; 034import org.apache.commons.rdf.api.RDF; 035import org.apache.commons.rdf.api.Triple; 036import org.apache.commons.rdf.api.TripleLike; 037import org.apache.commons.rdf.jena.impl.InternalJenaFactory; 038import org.apache.jena.datatypes.RDFDatatype; 039import org.apache.jena.datatypes.xsd.XSDDatatype; 040import org.apache.jena.graph.Node; 041import org.apache.jena.graph.NodeFactory; 042import org.apache.jena.query.DatasetFactory; 043import org.apache.jena.riot.Lang; 044import org.apache.jena.riot.RDFDataMgr; 045import org.apache.jena.riot.RDFLanguages; 046import org.apache.jena.riot.system.StreamRDF; 047import org.apache.jena.riot.system.StreamRDFBase; 048import org.apache.jena.sparql.core.DatasetGraph; 049import org.apache.jena.sparql.core.DatasetGraphFactory; 050import org.apache.jena.sparql.graph.GraphFactory; 051 052/** 053 * Apache Jena RDF implementation. 054 * <p> 055 * Instances of JenaRDF can also convert existing objects from Jena with methods 056 * like {@link #asRDFTerm(Node)} and 057 * {@link #asGraph(org.apache.jena.graph.Graph)}, and vice versa from any 058 * Commons RDF object to Jena with the <code>asJena*</code> methods like 059 * {@link #asJenaNode(RDFTerm)} and {@link #asJenaGraph(Graph)}. 060 * <p> 061 * Note that Commons RDF objects created by this class implement the 062 * specializations interfaces like {@link JenaRDFTerm}, {@link JenaGraph} and 063 * {@link JenaTriple}, which provide access to the underlying Jena objects, e.g. 064 * with {@link JenaRDFTerm#asJenaNode()}. 065 * <p> 066 * For the purpose of {@link BlankNode} identity when using 067 * {@link #createBlankNode(String)} (see {@link BlankNode#equals(Object)} and 068 * {@link BlankNode#uniqueReference()}), each instance of JenaRDF uses an 069 * internal random state. If for some reason consistent/reproducible BlankNode 070 * identity is desired, it is possible to retrieve the state as a UUID using 071 * {@link #salt()} for subsequent use with {@link JenaRDF#JenaRDF(UUID)} - note 072 * that such consistency is only guaranteed within the same minor version of 073 * Commons RDF. 074 * 075 * @see RDF 076 */ 077public final class JenaRDF implements RDF { 078 079 private static InternalJenaFactory internalJenaFactory = new InternalJenaFactory() { 080 }; 081 082 private final UUID salt; 083 084 /** 085 * Create a JenaRDF. 086 * <p> 087 * This constructor will use a randomly generated {@link UUID} as a salt for 088 * the purposes of {@link BlankNode} identity, see {@link #salt()}. 089 */ 090 public JenaRDF() { 091 this.salt = UUID.randomUUID(); 092 } 093 094 /** 095 * Create a JenaRDF. 096 * <p> 097 * This constructor will use the specified {@link UUID} as a salt for the 098 * purposes of {@link BlankNode} identity, and should only be used in cases 099 * where predictable and consistent {@link BlankNode#uniqueReference()} are 100 * important. 101 * 102 * @param salt 103 * {@link UUID} to use as salt for {@link BlankNode} equality 104 */ 105 public JenaRDF(final UUID salt) { 106 this.salt = salt; 107 } 108 109 @Override 110 public JenaBlankNode createBlankNode() { 111 return internalJenaFactory.createBlankNode(salt()); 112 } 113 114 @Override 115 public JenaBlankNode createBlankNode(final String name) { 116 return internalJenaFactory.createBlankNode(name, salt()); 117 } 118 119 @Override 120 public JenaDataset createDataset() { 121 return internalJenaFactory.createDataset(salt()); 122 } 123 124 @Override 125 public JenaGraph createGraph() { 126 return internalJenaFactory.createGraph(salt()); 127 } 128 129 @Override 130 public JenaIRI createIRI(final String iri) { 131 validateIRI(iri); 132 return internalJenaFactory.createIRI(iri); 133 } 134 135 @Override 136 public JenaLiteral createLiteral(final String lexicalForm) { 137 return internalJenaFactory.createLiteral(lexicalForm); 138 } 139 140 @Override 141 public JenaLiteral createLiteral(final String lexicalForm, final IRI dataType) { 142 return internalJenaFactory.createLiteralDT(lexicalForm, dataType.getIRIString()); 143 } 144 145 @Override 146 public JenaLiteral createLiteral(final String lexicalForm, final String languageTag) { 147 validateLang(languageTag); 148 return internalJenaFactory.createLiteralLang(lexicalForm, languageTag); 149 } 150 151 @Override 152 public JenaTriple createTriple(final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) { 153 return internalJenaFactory.createTriple(subject, predicate, object); 154 } 155 156 /** 157 * {@inheritDoc} 158 * <p> 159 * In addition to supporting a <code>graphName</code> of <code>null</code> 160 * for representing a triple in the <em>default graph</em>, this method also 161 * recognize a {@link JenaIRI} which {@link JenaRDFTerm#asJenaNode()} 162 * represent the default graph according to 163 * {@link org.apache.jena.sparql.core.Quad#isDefaultGraph(Node)}, in which 164 * case the returned JenaQuad will have a {@link Quad#getGraphName()} of 165 * {@link Optional#empty()} rather than the provided IRI. 166 * 167 */ 168 @Override 169 public JenaQuad createQuad(final BlankNodeOrIRI graphName, final BlankNodeOrIRI subject, final IRI predicate, final RDFTerm object) 170 throws IllegalArgumentException, UnsupportedOperationException { 171 return internalJenaFactory.createQuad(subject, predicate, object, graphName); 172 } 173 174 /** 175 * Create a generalized Jena triple. 176 * <p> 177 * The <em>generalized triple</em> supports any {@link RDFTerm} as its 178 * {@link TripleLike#getSubject()} {@link TripleLike#getPredicate()} or 179 * {@link TripleLike#getObject()}. 180 * 181 * @see #createTriple(BlankNodeOrIRI, IRI, RDFTerm) 182 * @see #createGeneralizedQuad(RDFTerm, RDFTerm, RDFTerm, RDFTerm) 183 * 184 * @param subject 185 * The subject of the statement 186 * @param predicate 187 * The predicate of the statement 188 * @param object 189 * The object of the statement 190 * @return Generalized {@link TripleLike}. Note that the generalized triple 191 * does <strong>not</strong> implement {@link Triple#equals(Object)} 192 * or {@link Triple#hashCode()}. 193 */ 194 public JenaGeneralizedTripleLike createGeneralizedTriple(final RDFTerm subject, final RDFTerm predicate, final RDFTerm object) { 195 return internalJenaFactory.createGeneralizedTriple(subject, predicate, object); 196 } 197 198 /** 199 * Create a generalized Jena quad. 200 * <p> 201 * The <em>generalized quad</em> supports any {@link RDFTerm} as its 202 * {@link QuadLike#getSubject()} {@link QuadLike#getPredicate()}, 203 * {@link QuadLike#getObject()} or {@link QuadLike#getObject()}. 204 * <p> 205 * In addition to supporting a <code>graphName</code> of <code>null</code> 206 * for representing a triple in the <em>default graph</em>, this method also 207 * recognize a {@link JenaIRI} which {@link JenaRDFTerm#asJenaNode()} 208 * represent the default graph according to 209 * {@link org.apache.jena.sparql.core.Quad#isDefaultGraph(Node)}, in which 210 * case the returned JenaQuad will have a {@link Quad#getGraphName()} of 211 * {@link Optional#empty()} rather than the provided IRI. 212 * 213 * @see #createQuad(BlankNodeOrIRI, BlankNodeOrIRI, IRI, RDFTerm) 214 * @see #createGeneralizedTriple(RDFTerm, RDFTerm, RDFTerm) 215 * 216 * @param subject 217 * The subject of the statement 218 * @param predicate 219 * The predicate of the statement 220 * @param object 221 * The object of the statement 222 * @param graphName 223 * The graph name of the statement 224 * @return Generalized {@link QuadLike}. Note that the generalized quad does 225 * <strong>not</strong> implement {@link Quad#equals(Object)} or 226 * {@link Quad#hashCode()}. 227 */ 228 public JenaGeneralizedQuadLike createGeneralizedQuad(final RDFTerm subject, final RDFTerm predicate, final RDFTerm object, 229 final RDFTerm graphName) { 230 return internalJenaFactory.createGeneralizedQuad(subject, predicate, object, graphName); 231 } 232 233 /** 234 * Adapt an existing Jena Node to CommonsRDF {@link RDFTerm}. 235 * <p> 236 * If {@link Node#isLiteral()}, then the returned value is a 237 * {@link Literal}. If {@link Node#isURI()}, the returned value is a IRI. If 238 * {$@link Node#isBlank()}, the returned value is a {@link BlankNode}, which 239 * will use a {@link UUID} salt from this {@link JenaRDF} instance in 240 * combination with {@link Node#getBlankNodeId()} for the purpose of its 241 * {@link BlankNode#uniqueReference()}. 242 * 243 * @see #asRDFTerm(RDF, Node) 244 * 245 * @param node 246 * The Jena Node to adapt. It's {@link Node#isConcrete()} must be 247 * <code>true</code>. 248 * @return Adapted {@link JenaRDFTerm} 249 * @throws ConversionException 250 * If the {@link Node} can't be represented as an 251 * {@link RDFTerm}, e.g. if the node is not concrete or 252 * represents a variable in Jena. 253 */ 254 public JenaRDFTerm asRDFTerm(final Node node) throws ConversionException { 255 return internalJenaFactory.createRDFTerm(node, salt()); 256 } 257 258 /** 259 * Convert from Jena {@link Node} to any Commons RDF implementation. 260 * <p> 261 * Note that if the {@link Node#isBlank()}, then the factory's 262 * {@link RDF#createBlankNode(String)} will be used, meaning that care 263 * should be taken if reusing an {@link RDF} instance for multiple 264 * conversion sessions. 265 * 266 * @see #asRDFTerm(Node) 267 * 268 * @param factory 269 * {@link RDF} to use for creating {@link RDFTerm}. 270 * @param node 271 * The Jena Node to adapt. It's {@link Node#isConcrete()} must be 272 * <code>true</code>. 273 * @return Adapted {@link RDFTerm} 274 * @throws ConversionException 275 * If the {@link Node} can't be represented as an 276 * {@link RDFTerm}, e.g. if the node is not concrete or 277 * represents a variable in Jena. 278 */ 279 public static RDFTerm asRDFTerm(final RDF factory, final Node node) { 280 if (node == null) { 281 return null; 282 } 283 if (factory instanceof JenaRDF) { 284 // No need to convert, just wrap 285 return ((JenaRDF) factory).asRDFTerm(node); 286 } 287 if (node.isURI()) { 288 return factory.createIRI(node.getURI()); 289 } 290 if (node.isLiteral()) { 291 final String lang = node.getLiteralLanguage(); 292 if (lang != null && !lang.isEmpty()) { 293 return factory.createLiteral(node.getLiteralLexicalForm(), lang); 294 } 295 if (node.getLiteralDatatype().equals(XSDDatatype.XSDstring)) { 296 return factory.createLiteral(node.getLiteralLexicalForm()); 297 } 298 final IRI dt = factory.createIRI(node.getLiteralDatatype().getURI()); 299 return factory.createLiteral(node.getLiteralLexicalForm(), dt); 300 } 301 if (node.isBlank()) { 302 // The factory 303 return factory.createBlankNode(node.getBlankNodeLabel()); 304 } 305 throw new ConversionException("Node is not a concrete RDF Term: " + node); 306 } 307 308 /** 309 * Adapt an existing Jena Triple to CommonsRDF {@link Triple}. 310 * <p> 311 * If the triple contains any {@link Node#isBlank()}, then any corresponding 312 * {@link BlankNode} will use a {@link UUID} salt from this {@link JenaRDF} 313 * instance in combination with {@link Node#getBlankNodeId()} for the 314 * purpose of its {@link BlankNode#uniqueReference()}. 315 * 316 * @see #asTriple(RDF, org.apache.jena.graph.Triple) 317 * 318 * @param triple 319 * Jena {@link org.apache.jena.graph.Triple} to adapt 320 * @return Adapted {@link JenaTriple} 321 * @throws ConversionException 322 * if any of the triple's nodes are not concrete or the triple 323 * is a generalized triple 324 */ 325 public JenaTriple asTriple(final org.apache.jena.graph.Triple triple) throws ConversionException { 326 return internalJenaFactory.createTriple(triple, salt()); 327 } 328 329 /** 330 * Adapt a generalized Jena {@link org.apache.jena.graph.Triple} to a 331 * CommonsRDF {@link TripleLike}. 332 * <p> 333 * The generalized triple supports any {@link RDFTerm} as its 334 * {@link TripleLike#getSubject()} {@link TripleLike#getPredicate()} or 335 * {@link TripleLike#getObject()}. 336 * <p> 337 * If the Jena triple contains any {@link Node#isBlank()}, then any 338 * corresponding {@link BlankNode} will use a {@link UUID} salt from this 339 * {@link JenaRDF} instance in combination with 340 * {@link Node#getBlankNodeId()} for the purpose of its 341 * {@link BlankNode#uniqueReference()}. 342 * 343 * @see #asTriple(RDF, org.apache.jena.graph.Triple) 344 * 345 * @param triple 346 * Jena triple 347 * @return Adapted {@link TripleLike}. Note that the generalized triple does 348 * <strong>not</strong> implement {@link Triple#equals(Object)} or 349 * {@link Triple#hashCode()}. 350 * @throws ConversionException 351 * if any of the triple's nodes are not concrete 352 */ 353 public JenaTripleLike asGeneralizedTriple(final org.apache.jena.graph.Triple triple) throws ConversionException { 354 return internalJenaFactory.createGeneralizedTriple(triple, salt()); 355 } 356 357 /** 358 * Adapt a generalized Jena {@link org.apache.jena.sparql.core.Quad} to a 359 * CommonsRDF {@link QuadLike}. 360 * <p> 361 * The generalized quad supports any {@link RDFTerm} as its 362 * {@link QuadLike#getGraphName()}, {@link QuadLike#getSubject()} 363 * {@link QuadLike#getPredicate()} or {@link QuadLike#getObject()}. 364 * <p> 365 * If the Jena quad contains any {@link Node#isBlank()}, then any 366 * corresponding {@link BlankNode} will use a {@link UUID} salt from this 367 * {@link JenaRDF} instance in combination with 368 * {@link Node#getBlankNodeId()} for the purpose of its 369 * {@link BlankNode#uniqueReference()}. 370 * <p> 371 * If the provided quad {@link org.apache.jena.sparql.core.Quad#isDefaultGraph()}, 372 * the returned {@link JenaQuadLike} has a {@link JenaQuadLike#getGraphName()} 373 * of {@link Optional#empty()}. 374 * 375 * @see #asQuad(org.apache.jena.sparql.core.Quad) 376 * @see #asGeneralizedTriple(org.apache.jena.graph.Triple) 377 * 378 * @param quad 379 * Jena quad 380 * @return Adapted {@link QuadLike}. Note that the generalized quad does 381 * <strong>not</strong> implement {@link Quad#equals(Object)} or 382 * {@link Quad#hashCode()}. 383 * @throws ConversionException 384 * if any of the quad nodes are not concrete 385 */ 386 public JenaQuadLike<RDFTerm> asGeneralizedQuad(final org.apache.jena.sparql.core.Quad quad) throws ConversionException { 387 return internalJenaFactory.createGeneralizedQuad(quad, salt()); 388 } 389 390 /** 391 * Convert from Jena {@link org.apache.jena.graph.Triple} to a Commons RDF 392 * {@link Triple}. 393 * <p> 394 * Note that if any of the triple's nodes {@link Node#isBlank()}, then the 395 * factory's {@link RDF#createBlankNode(String)} will be used, meaning that 396 * care should be taken if reusing an {@link RDF} instance for multiple 397 * conversion sessions. 398 * 399 * @see #asTriple(org.apache.jena.graph.Triple) 400 * 401 * @param factory 402 * {@link RDF} to use for creating the {@link Triple} and its 403 * {@link RDFTerm}s. 404 * @param triple 405 * Jena triple 406 * @return Converted triple 407 * @throws ConversionException 408 * if any of the triple's nodes are not concrete or the triple 409 * is a generalized triple 410 */ 411 public static Triple asTriple(final RDF factory, final org.apache.jena.graph.Triple triple) throws ConversionException { 412 if (factory instanceof JenaRDF) { 413 // No need to convert, just wrap 414 return ((JenaRDF) factory).asTriple(triple); 415 } 416 final BlankNodeOrIRI subject; 417 final IRI predicate; 418 try { 419 subject = (BlankNodeOrIRI) asRDFTerm(factory, triple.getSubject()); 420 predicate = (IRI) asRDFTerm(factory, triple.getPredicate()); 421 } catch (final ClassCastException ex) { 422 throw new ConversionException("Can't convert generalized triple: " + triple, ex); 423 } 424 final RDFTerm object = asRDFTerm(factory, triple.getObject()); 425 return factory.createTriple(subject, predicate, object); 426 } 427 428 /** 429 * Adapt an existing Jena {@link org.apache.jena.sparql.core.Quad} to 430 * CommonsRDF {@link Quad}. 431 * <p> 432 * If the quad contains any {@link Node#isBlank()}, then any corresponding 433 * {@link BlankNode} will use a {@link UUID} salt from this {@link JenaRDF} 434 * instance in combination with {@link Node#getBlankNodeId()} for the 435 * purpose of its {@link BlankNode#uniqueReference()}. 436 * <p> 437 * If the provided quad {@link org.apache.jena.sparql.core.Quad#isDefaultGraph()}, 438 * the returned {@link JenaQuad} has a {@link Quad#getGraphName()} 439 * of {@link Optional#empty()}. 440 * 441 * @param quad 442 * Jena quad 443 * @return Adapted quad 444 */ 445 public JenaQuad asQuad(final org.apache.jena.sparql.core.Quad quad) { 446 return internalJenaFactory.createQuad(quad, salt()); 447 } 448 449 /** 450 * Adapt an existing Jena {@link org.apache.jena.graph.Graph} to CommonsRDF 451 * {@link Graph}. 452 * <p> 453 * This does not take a copy, changes to the CommonsRDF Graph are reflected 454 * in the jena graph, which is accessible from 455 * {@link JenaGraph#asJenaGraph()}. 456 * <p> 457 * If the graph contains any {@link Node#isBlank()}, then any corresponding 458 * {@link BlankNode} will use a {@link UUID} salt from this {@link JenaRDF} 459 * instance in combination with {@link Node#getBlankNodeId()} for the 460 * purpose of its {@link BlankNode#uniqueReference()}. 461 * 462 * @param graph 463 * Jena {@link org.apache.jena.graph.Graph} to adapt 464 * @return Adapted {@link JenaGraph} 465 */ 466 public JenaGraph asGraph(final org.apache.jena.graph.Graph graph) { 467 return internalJenaFactory.createGraph(graph, salt()); 468 } 469 470 /** 471 * Adapt an existing Jena {@link org.apache.jena.rdf.model.Model} to 472 * CommonsRDF {@link Graph}. 473 * <p> 474 * This does not ake a copy, changes to the CommonsRDF Graph are reflected 475 * in the jena graph, which is accessible from 476 * {@link JenaGraph#asJenaGraph()}. 477 * <p> 478 * If the graph contains any {@link Node#isBlank()}, then any corresponding 479 * {@link BlankNode} will use a {@link UUID} salt from this {@link JenaRDF} 480 * instance in combination with {@link Node#getBlankNodeId()} for the 481 * purpose of its {@link BlankNode#uniqueReference()}. 482 * 483 * @param model 484 * Jena {@link org.apache.jena.rdf.model.Model} to adapt 485 * @return Adapted {@link JenaGraph} 486 */ 487 public JenaGraph asGraph(final org.apache.jena.rdf.model.Model model) { 488 return internalJenaFactory.createGraph(model, salt()); 489 } 490 491 /** 492 * Adapt an existing Jena {@link DatasetGraph} to CommonsRDF 493 * {@link Dataset}. 494 * <p> 495 * This does not take a copy, changes to the CommonsRDF Dataset are 496 * reflected in the jena dataset graph, which is accessible from 497 * {@link JenaDataset#asJenaDatasetGraph()}. 498 * <p> 499 * If the dataset contains any {@link Node#isBlank()}, then any 500 * corresponding {@link BlankNode} will use a {@link UUID} salt from this 501 * {@link JenaRDF} instance in combination with 502 * {@link Node#getBlankNodeId()} for the purpose of its 503 * {@link BlankNode#uniqueReference()}. 504 * 505 * @param datasetGraph 506 * Jena {@link DatasetGraph} to adapt 507 * @return Adapted {@link JenaDataset} 508 */ 509 public JenaDataset asDataset(final DatasetGraph datasetGraph) { 510 return internalJenaFactory.createDataset(datasetGraph, salt()); 511 } 512 513 /** 514 * Adapt an existing Jena {@link org.apache.jena.query.Dataset} to 515 * CommonsRDF {@link Dataset}. 516 * <p> 517 * This does not take a copy, changes to the CommonsRDF Dataset are 518 * reflected in the jena dataset graph, which is accessible from 519 * {@link JenaDataset#asJenaDatasetGraph()}. 520 * <p> 521 * If the dataset contains any {@link Node#isBlank()}, then any 522 * corresponding {@link BlankNode} will use a {@link UUID} salt from this 523 * {@link JenaRDF} instance in combination with 524 * {@link Node#getBlankNodeId()} for the purpose of its 525 * {@link BlankNode#uniqueReference()}. 526 * 527 * @param datasetGraph 528 * Jena {@link org.apache.jena.query.Dataset} to adapt 529 * @return Adapted {@link JenaDataset} 530 */ 531 public JenaDataset asDataset(final org.apache.jena.query.Dataset datasetGraph) { 532 return internalJenaFactory.createDataset(datasetGraph.asDatasetGraph(), salt()); 533 } 534 535 /** 536 * Convert from Jena {@link org.apache.jena.sparql.core.Quad} to a Commons 537 * RDF {@link Quad}. 538 * <p> 539 * Note that if any of the quad's nodes {@link Node#isBlank()}, then the 540 * factory's {@link RDF#createBlankNode(String)} will be used, meaning that 541 * care should be taken if reusing an {@link RDF} instance for multiple 542 * conversion sessions. 543 * <p> 544 * If the provided quad {@link org.apache.jena.sparql.core.Quad#isDefaultGraph()}, 545 * the returned {@link JenaQuadLike} has a {@link JenaQuadLike#getGraphName()} 546 * of {@link Optional#empty()}. 547 * 548 * @see #asQuad(org.apache.jena.sparql.core.Quad) 549 * @see #asGeneralizedQuad(org.apache.jena.sparql.core.Quad) 550 * 551 * @param factory 552 * {@link RDF} to use for creating the {@link Triple} and its 553 * {@link RDFTerm}s. 554 * @param quad 555 * Jena {@link org.apache.jena.sparql.core.Quad} to adapt 556 * @return Converted {@link Quad} 557 * @throws ConversionException 558 * if any of the quad's nodes are not concrete or the quad is a 559 * generalized quad 560 */ 561 public static Quad asQuad(final RDF factory, final org.apache.jena.sparql.core.Quad quad) { 562 if (factory instanceof JenaRDF) { 563 // No need to convert, just wrap 564 return ((JenaRDF) factory).asQuad(quad); 565 } 566 final BlankNodeOrIRI graphName = (BlankNodeOrIRI) (asRDFTerm(factory, quad.getGraph())); 567 final BlankNodeOrIRI subject = (BlankNodeOrIRI) (asRDFTerm(factory, quad.getSubject())); 568 final IRI predicate = (IRI) (asRDFTerm(factory, quad.getPredicate())); 569 final RDFTerm object = asRDFTerm(factory, quad.getObject()); 570 return factory.createQuad(graphName, subject, predicate, object); 571 } 572 573 /** 574 * Return {@link RDFSyntax} corresponding to a Jena {@link Lang}. 575 * 576 * @param lang 577 * {@link Lang} to convert 578 * @return Matched {@link RDFSyntax}, otherwise {@link Optional#empty()} 579 */ 580 public Optional<RDFSyntax> asRDFSyntax(final Lang lang) { 581 return RDFSyntax.byMediaType(lang.getContentType().getContentType()); 582 } 583 584 /** 585 * Return Jena {@link Lang} corresponding to a {@link RDFSyntax}. 586 * 587 * @param rdfSyntax 588 * {@link RDFSyntax} to convert 589 * @return Matched {@link Lang}, otherwise {@link Optional#empty()} 590 */ 591 public Optional<Lang> asJenaLang(final RDFSyntax rdfSyntax) { 592 return Optional.ofNullable(RDFLanguages.contentTypeToLang(rdfSyntax.mediaType())); 593 } 594 595 /** 596 * Create a {@link StreamRDF} instance that inserts the converted 597 * {@link Quad}s. into a the provided {@link Consumer}. 598 * <p> 599 * The returned {@link StreamRDF} can be used for instance with Jena's 600 * {@link RDFDataMgr#parse(StreamRDF, String)}. 601 * 602 * @param factory 603 * {@link RDF} to use for creating {@link RDFTerm}s and 604 * {@link Quad}s. 605 * @param consumer 606 * A {@link Consumer} of {@link Quad}s 607 * @return A {@link StreamRDF} that will stream converted quads to the 608 * consumer 609 */ 610 public static StreamRDF streamJenaToQuad(final RDF factory, final Consumer<Quad> consumer) { 611 return new StreamRDFBase() { 612 @Override 613 public void quad(final org.apache.jena.sparql.core.Quad quad) { 614 consumer.accept(asQuad(factory, quad)); 615 } 616 }; 617 } 618 619 /** 620 * Create a {@link StreamRDF} instance that inserts generalized 621 * {@link TripleLike}s. into a the provided {@link Consumer}. 622 * <p> 623 * A generalized triple allows any {@link RDFTerm} for 624 * {@link TripleLike#getSubject()}, {@link TripleLike#getPredicate()} and 625 * {@link TripleLike#getObject()}. 626 * <p> 627 * The returned {@link StreamRDF} can be used for instance with Jena's 628 * {@link RDFDataMgr#parse(StreamRDF, String)}. 629 * 630 * @param generalizedConsumer 631 * A {@link Consumer} of generalized {@link TripleLike}s 632 * @return A {@link StreamRDF} that will stream generalized triples to the 633 * consumer 634 */ 635 public StreamRDF streamJenaToGeneralizedTriple(final Consumer<TripleLike> generalizedConsumer) { 636 return new StreamRDFBase() { 637 @Override 638 public void triple(final org.apache.jena.graph.Triple triple) { 639 generalizedConsumer.accept(asGeneralizedTriple(triple)); 640 } 641 }; 642 } 643 644 /** 645 * Create a {@link StreamRDF} instance that inserts generalized 646 * {@link QuadLike}s. into a the provided {@link Consumer}. 647 * <p> 648 * A generalized quad allows any {@link RDFTerm} for 649 * {@link QuadLike#getSubject()}, {@link TripleLike#getPredicate()}, 650 * {@link QuadLike#getObject()} and {@link QuadLike#getGraphName()} . 651 * <p> 652 * The returned {@link StreamRDF} can be used for instance with Jena's 653 * {@link RDFDataMgr#parse(StreamRDF, String)}. 654 * 655 * @param generalizedConsumer 656 * A {@link Consumer} of generalized {@link QuadLike}s 657 * @return A {@link StreamRDF} that will stream generalized quads to the 658 * consumer 659 */ 660 public StreamRDF streamJenaToGeneralizedQuad(final Consumer<QuadLike<RDFTerm>> generalizedConsumer) { 661 return new StreamRDFBase() { 662 @Override 663 public void quad(final org.apache.jena.sparql.core.Quad quad) { 664 generalizedConsumer.accept(asGeneralizedQuad(quad)); 665 } 666 }; 667 } 668 669 /** 670 * Convert a CommonsRDF Dataset to a Jena Dataset. If the Dataset was from Jena 671 * originally, return that original object wrapped else create a copy using Jena 672 * objects. 673 * 674 * @param dataset 675 * Commons RDF {@link Dataset} to convert 676 * @return Converted Jena {@link org.apache.jena.query.Dataset} 677 */ 678 public org.apache.jena.query.Dataset asJenaDataset(final Dataset dataset) { 679 return DatasetFactory.wrap(asJenaDatasetGraph(dataset)); 680 } 681 682 /** 683 * Convert a CommonsRDF Dataset to a Jena DatasetGraph. If the Dataset was from Jena 684 * originally, return that original object's underlying DatasetGraph else create a 685 * copy using Jena objects. 686 * 687 * @param dataset 688 * Commons RDF {@link Dataset} to convert 689 * @return Converted Jena {@link org.apache.jena.sparql.core.DatasetGraph} 690 */ 691 public DatasetGraph asJenaDatasetGraph(final Dataset dataset) { 692 final DatasetGraph dsg; 693 if (dataset instanceof JenaDataset) 694 dsg = ((JenaDataset) dataset).asJenaDatasetGraph(); 695 else { 696 dsg = DatasetGraphFactory.createGeneral(); 697 dataset.stream().map(this::asJenaQuad).forEach(dsg::add); 698 } 699 return dsg; 700 } 701 702 /** 703 * Convert a CommonsRDF Graph to a Jena Graph. If the Graph was from Jena 704 * originally, return that original object else create a copy using Jena 705 * objects. 706 * 707 * @param graph 708 * Commons RDF {@link Graph} to convert 709 * @return Converted Jena {@link org.apache.jena.graph.Graph} 710 */ 711 public org.apache.jena.graph.Graph asJenaGraph(final Graph graph) { 712 if (graph instanceof JenaGraph) { 713 return ((JenaGraph) graph).asJenaGraph(); 714 } 715 final org.apache.jena.graph.Graph g = GraphFactory.createGraphMem(); 716 graph.stream().forEach(t -> g.add(asJenaTriple(t))); 717 return g; 718 } 719 720 /** 721 * Convert a CommonsRDF RDFTerm to a Jena Node. If the RDFTerm was from Jena 722 * originally, return that original object, else create a copy using Jena 723 * objects. 724 * 725 * @param term 726 * Commons RDF {@link RDFTerm} to convert 727 * @return Converted Jena {@link Node} 728 */ 729 public Node asJenaNode(final RDFTerm term) { 730 if (term == null) { 731 return null; 732 } 733 if (term instanceof JenaRDFTerm) { 734 // TODO: What if it's a JenaBlankNodeImpl with 735 // a different salt? Do we need to rewrite the 736 // jena blanknode identifier? 737 return ((JenaRDFTerm) term).asJenaNode(); 738 } 739 740 if (term instanceof IRI) { 741 return NodeFactory.createURI(((IRI) term).getIRIString()); 742 } 743 744 if (term instanceof Literal) { 745 final Literal lit = (Literal) term; 746 final RDFDatatype dt = NodeFactory.getType(lit.getDatatype().getIRIString()); 747 final String lang = lit.getLanguageTag().orElse(""); 748 return NodeFactory.createLiteral(lit.getLexicalForm(), lang, dt); 749 } 750 751 if (term instanceof BlankNode) { 752 final String id = ((BlankNode) term).uniqueReference(); 753 return NodeFactory.createBlankNode(id); 754 } 755 throw new ConversionException("Not a concrete RDF Term: " + term); 756 } 757 758 /** 759 * Convert a CommonsRDF {@link Triple} to a Jena 760 * {@link org.apache.jena.graph.Triple}. 761 * <p> 762 * If the triple was from Jena originally, return that original object, else 763 * create a copy using Jena objects. 764 * 765 * @param triple 766 * Commons RDF {@link Triple} to convert 767 * @return Converted Jena {@link org.apache.jena.graph.Triple} 768 */ 769 public org.apache.jena.graph.Triple asJenaTriple(final Triple triple) { 770 if (triple instanceof JenaTriple) { 771 return ((JenaTriple) triple).asJenaTriple(); 772 } 773 return org.apache.jena.graph.Triple.create(asJenaNode(triple.getSubject()), 774 asJenaNode(triple.getPredicate()), 775 asJenaNode(triple.getObject())); 776 } 777 778 /** 779 * Convert a CommonsRDF {@link Quad} to a Jena 780 * {@link org.apache.jena.sparql.core.Quad}. 781 * <p> 782 * If the quad was from Jena originally, return that original object, 783 * otherwise create a copy using Jena objects. 784 * 785 * @param quad 786 * Commons RDF {@link Quad} to convert 787 * @return Converted Jena {@link org.apache.jena.sparql.core.Quad} 788 */ 789 public org.apache.jena.sparql.core.Quad asJenaQuad(final Quad quad) { 790 if (quad instanceof JenaQuad) { 791 return ((JenaQuad) quad).asJenaQuad(); 792 } 793 return org.apache.jena.sparql.core.Quad.create( 794 asJenaNode(quad.getGraphName().orElse(null)), 795 asJenaNode(quad.getSubject()), 796 asJenaNode(quad.getPredicate()), 797 asJenaNode(quad.getObject())); 798 } 799 800 // Some simple validations - full IRI parsing is not cheap. 801 private void validateIRI(final String iri) { 802 if (iri.contains(" ")) { 803 throw new IllegalArgumentException(); 804 } 805 if (iri.contains("<")) { 806 throw new IllegalArgumentException(); 807 } 808 if (iri.contains(">")) { 809 throw new IllegalArgumentException(); 810 } 811 } 812 813 private static void validateLang(final String languageTag) { 814 if (languageTag.contains(" ")) { 815 throw new IllegalArgumentException("Invalid language tag: " + languageTag); 816 } 817 } 818 819 /** 820 * Return the {@link UUID} salt used by this factory. 821 * <p> 822 * The salt is used for the purposes of {@link BlankNode} identity, see 823 * {@link BlankNode#uniqueReference()} for details. 824 * <p> 825 * This salt can be used with the constructor {@link JenaRDF#JenaRDF(UUID)} 826 * if consistent or reproducible {@link BlankNode}s are desirable. 827 * 828 * @return The {@link UUID} used as salt 829 */ 830 public UUID salt() { 831 return salt; 832 } 833 834}