001// *************************************************************************************************************************** 002// * Licensed to the Apache Software Foundation (ASF) under one or more contributor license agreements. See the NOTICE file * 003// * distributed with this work for additional information regarding copyright ownership. The ASF licenses this file * 004// * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance * 005// * with the License. You may obtain a copy of the License at * 006// * * 007// * http://www.apache.org/licenses/LICENSE-2.0 * 008// * * 009// * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an * 010// * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the * 011// * specific language governing permissions and limitations under the License. * 012// *************************************************************************************************************************** 013package org.apache.juneau.jena; 014 015import static org.apache.juneau.internal.CollectionUtils.*; 016 017import java.util.*; 018 019import org.apache.juneau.*; 020import org.apache.juneau.internal.*; 021import org.apache.juneau.jena.annotation.*; 022import org.apache.juneau.serializer.*; 023import org.apache.juneau.xml.*; 024import org.apache.juneau.xml.annotation.*; 025 026/** 027 * Serializes POJOs to RDF. 028 * 029 * <h5 class='topic'>Behavior-specific subclasses</h5> 030 * 031 * The following direct subclasses are provided for language-specific serializers: 032 * <ul> 033 * <li>{@link RdfXmlSerializer} - RDF/XML. 034 * <li>{@link RdfXmlAbbrevSerializer} - RDF/XML-ABBREV. 035 * <li>{@link NTripleSerializer} - N-TRIPLE. 036 * <li>{@link TurtleSerializer} - TURTLE. 037 * <li>{@link N3Serializer} - N3. 038 * </ul> 039 * 040 * <h5 class='section'>See Also:</h5> 041 * <ul> 042 * <li class='link'>{@doc juneau-marshall-rdf} 043 * </ul> 044 */ 045public class RdfSerializer extends WriterSerializer implements RdfCommon { 046 047 private static final Namespace 048 DEFAULT_JUNEAU_NS = Namespace.create("j", "http://www.apache.org/juneau/"), 049 DEFAULT_JUNEAUBP_NS = Namespace.create("jp", "http://www.apache.org/juneaubp/"); 050 051 //------------------------------------------------------------------------------------------------------------------- 052 // Configurable properties 053 //------------------------------------------------------------------------------------------------------------------- 054 055 private static final String PREFIX = "RdfSerializer."; 056 057 /** 058 * Configuration property: Add <js>"_type"</js> properties when needed. 059 * 060 * <h5 class='section'>Property:</h5> 061 * <ul> 062 * <li><b>Name:</b> <js>"RdfSerializer.addBeanTypes.b"</js> 063 * <li><b>Data type:</b> <code>Boolean</code> 064 * <li><b>Default:</b> <jk>false</jk> 065 * <li><b>Session property:</b> <jk>false</jk> 066 * <li><b>Methods:</b> 067 * <ul> 068 * <li class='jm'>{@link RdfSerializerBuilder#addBeanTypes(boolean)} 069 * </ul> 070 * </ul> 071 * 072 * <h5 class='section'>Description:</h5> 073 * <p> 074 * If <jk>true</jk>, then <js>"_type"</js> properties will be added to beans if their type cannot be inferred 075 * through reflection. 076 * 077 * <p> 078 * When present, this value overrides the {@link #SERIALIZER_addBeanTypes} setting and is 079 * provided to customize the behavior of specific serializers in a {@link SerializerGroup}. 080 */ 081 public static final String RDF_addBeanTypes = PREFIX + "addBeanTypes.b"; 082 083 /** 084 * Configuration property: Add XSI data types to non-<code>String</code> literals. 085 * 086 * <h5 class='section'>Property:</h5> 087 * <ul> 088 * <li><b>Name:</b> <js>"RdfSerializer.addLiteralTypes.b"</js> 089 * <li><b>Data type:</b> <code>Boolean</code> 090 * <li><b>Default:</b> <jk>false</jk> 091 * <li><b>Session property:</b> <jk>false</jk> 092 * <li><b>Methods:</b> 093 * <ul> 094 * <li class='jm'>{@link RdfSerializerBuilder#addLiteralTypes(boolean)} 095 * <li class='jm'>{@link RdfSerializerBuilder#addLiteralTypes()} 096 * </ul> 097 * </ul> 098 */ 099 public static final String RDF_addLiteralTypes = PREFIX + "addLiteralTypes.b"; 100 101 /** 102 * Configuration property: Add RDF root identifier property to root node. 103 * 104 * <h5 class='section'>Property:</h5> 105 * <ul> 106 * <li><b>Name:</b> <js>"RdfSerializer.addRootProperty.b"</js> 107 * <li><b>Data type:</b> <code>Boolean</code> 108 * <li><b>Default:</b> <jk>false</jk> 109 * <li><b>Session property:</b> <jk>false</jk> 110 * <li><b>Methods:</b> 111 * <ul> 112 * <li class='jm'>{@link RdfSerializerBuilder#addRootProperty(boolean)} 113 * <li class='jm'>{@link RdfSerializerBuilder#addRootProperty()} 114 * </ul> 115 * </ul> 116 * 117 * <h5 class='section'>Description:</h5> 118 * <p> 119 * When enabled an RDF property <code>http://www.apache.org/juneau/root</code> is added with a value of <js>"true"</js> 120 * to identify the root node in the graph. 121 * <br>This helps locate the root node during parsing. 122 * 123 * <p> 124 * If disabled, the parser has to search through the model to find any resources without incoming predicates to 125 * identify root notes, which can introduce a considerable performance degradation. 126 */ 127 public static final String RDF_addRootProperty = PREFIX + "addRootProperty.b"; 128 129 /** 130 * Configuration property: Auto-detect namespace usage. 131 * 132 * <h5 class='section'>Property:</h5> 133 * <ul> 134 * <li><b>Name:</b> <js>"RdfSerializer.autoDetectNamespaces.b"</js> 135 * <li><b>Data type:</b> <code>Boolean</code> 136 * <li><b>Default:</b> <jk>true</jk> 137 * <li><b>Session property:</b> <jk>false</jk> 138 * <li><b>Methods:</b> 139 * <ul> 140 * <li class='jm'>{@link RdfSerializerBuilder#autoDetectNamespaces(boolean)} 141 * </ul> 142 * </ul> 143 * 144 * <h5 class='section'>Description:</h5> 145 * <p> 146 * Detect namespace usage before serialization. 147 * 148 * <p> 149 * If enabled, then the data structure will first be crawled looking for namespaces that will be encountered before 150 * the root element is serialized. 151 */ 152 public static final String RDF_autoDetectNamespaces = PREFIX + "autoDetectNamespaces.b"; 153 154 /** 155 * Configuration property: Default namespaces. 156 * 157 * <h5 class='section'>Property:</h5> 158 * <ul> 159 * <li><b>Name:</b> <js>"RdfSerializer.namespaces.ls"</js> 160 * <li><b>Data type:</b> <code>List<String></code> (serialized {@link Namespace} objects) 161 * <li><b>Default:</b> empty list 162 * <li><b>Session property:</b> <jk>false</jk> 163 * <li><b>Annotations:</b> 164 * <ul> 165 * <li class='ja'>{@link Rdf#namespace()} 166 * </ul> 167 * <li><b>Methods:</b> 168 * <ul> 169 * <li class='jm'>{@link RdfSerializerBuilder#namespaces(Namespace...)} 170 * </ul> 171 * </ul> 172 * 173 * <h5 class='section'>Description:</h5> 174 * <p> 175 * The default list of namespaces associated with this serializer. 176 */ 177 public static final String RDF_namespaces = PREFIX + "namespaces.ls"; 178 179 //------------------------------------------------------------------------------------------------------------------- 180 // Instance 181 //------------------------------------------------------------------------------------------------------------------- 182 183 private final boolean 184 addLiteralTypes, 185 addRootProperty, 186 useXmlNamespaces, 187 looseCollections, 188 autoDetectNamespaces, 189 addBeanTypes; 190 private final String rdfLanguage; 191 private final Namespace juneauNs; 192 private final Namespace juneauBpNs; 193 private final RdfCollectionFormat collectionFormat; 194 final Map<String,Object> jenaSettings; 195 final Namespace[] namespaces; 196 197 /** 198 * Constructor. 199 * 200 * @param ps 201 * The property store containing all the settings for this object. 202 * @param produces 203 * The media type that this serializer produces. 204 * @param accept 205 * The accept media types that the serializer can handle. 206 * <p> 207 * Can contain meta-characters per the <code>media-type</code> specification of {@doc RFC2616.section14.1} 208 * <p> 209 * If empty, then assumes the only media type supported is <code>produces</code>. 210 * <p> 211 * For example, if this serializer produces <js>"application/json"</js> but should handle media types of 212 * <js>"application/json"</js> and <js>"text/json"</js>, then the arguments should be: 213 * <p class='bcode w800'> 214 * <jk>super</jk>(ps, <js>"application/json"</js>, <js>"application/json,text/json"</js>); 215 * </p> 216 * <br>...or... 217 * <p class='bcode w800'> 218 * <jk>super</jk>(ps, <js>"application/json"</js>, <js>"*​/json"</js>); 219 * </p> 220 * <p> 221 * The accept value can also contain q-values. 222 */ 223 public RdfSerializer(PropertyStore ps, String produces, String accept) { 224 super(ps, produces, accept); 225 addLiteralTypes = getBooleanProperty(RDF_addLiteralTypes, false); 226 addRootProperty = getBooleanProperty(RDF_addRootProperty, false); 227 useXmlNamespaces = getBooleanProperty(RDF_useXmlNamespaces, true); 228 looseCollections = getBooleanProperty(RDF_looseCollections, false); 229 autoDetectNamespaces = getBooleanProperty(RDF_autoDetectNamespaces, true); 230 rdfLanguage = getStringProperty(RDF_language, "RDF/XML-ABBREV"); 231 juneauNs = getProperty(RDF_juneauNs, Namespace.class, DEFAULT_JUNEAU_NS); 232 juneauBpNs = getProperty(RDF_juneauBpNs, Namespace.class, DEFAULT_JUNEAUBP_NS); 233 collectionFormat = getProperty(RDF_collectionFormat, RdfCollectionFormat.class, RdfCollectionFormat.DEFAULT); 234 namespaces = getProperty(RDF_namespaces, Namespace[].class, new Namespace[0]); 235 addBeanTypes = getBooleanProperty(RDF_addBeanTypes, getBooleanProperty(SERIALIZER_addBeanTypes, false)); 236 237 Map<String,Object> m = new LinkedHashMap<>(); 238 for (String k : getPropertyKeys("RdfCommon")) 239 if (k.startsWith("jena.")) 240 m.put(k.substring(5), getProperty(k)); 241 jenaSettings = unmodifiableMap(m); 242 } 243 244 /** 245 * Constructor. 246 * 247 * @param ps 248 * The property store containing all the settings for this object. 249 */ 250 public RdfSerializer(PropertyStore ps) { 251 this(ps, "text/xml+rdf", (String)null); 252 } 253 254 255 @Override /* Context */ 256 public RdfSerializerBuilder builder() { 257 return new RdfSerializerBuilder(getPropertyStore()); 258 } 259 260 /** 261 * Instantiates a new clean-slate {@link RdfSerializerBuilder} object. 262 * 263 * <p> 264 * This is equivalent to simply calling <code><jk>new</jk> RdfSerializerBuilder()</code>. 265 * 266 * <p> 267 * Note that this method creates a builder initialized to all default settings, whereas {@link #builder()} copies 268 * the settings of the object called on. 269 * 270 * @return A new {@link RdfSerializerBuilder} object. 271 */ 272 public static RdfSerializerBuilder create() { 273 return new RdfSerializerBuilder(); 274 } 275 276 @Override /* Serializer */ 277 public WriterSerializerSession createSession(SerializerSessionArgs args) { 278 return new RdfSerializerSession(this, args); 279 } 280 281 //----------------------------------------------------------------------------------------------------------------- 282 // Properties 283 //----------------------------------------------------------------------------------------------------------------- 284 285 /** 286 * Configuration property: Add XSI data types to non-<code>String</code> literals. 287 * 288 * @see #RDF_addLiteralTypes 289 * @return 290 * <jk>true</jk> if XSI data types should be added to string literals. 291 */ 292 protected final boolean isAddLiteralTypes() { 293 return addLiteralTypes; 294 } 295 296 /** 297 * Configuration property: Add RDF root identifier property to root node. 298 * 299 * @see #RDF_addRootProperty 300 * @return 301 * <jk>true</jk> if RDF property <code>http://www.apache.org/juneau/root</code> is added with a value of <js>"true"</js> 302 * to identify the root node in the graph. 303 */ 304 protected final boolean isAddRootProp() { 305 return addRootProperty; 306 } 307 308 /** 309 * Configuration property: Reuse XML namespaces when RDF namespaces not specified. 310 * 311 * @see #RDF_useXmlNamespaces 312 * @return 313 * <jk>true</jk> if namespaces defined using {@link XmlNs @XmlNs} and {@link Xml @Xml} will be inherited by the RDF serializers. 314 * <br>Otherwise, namespaces will be defined using {@link RdfNs @RdfNs} and {@link Rdf @Rdf}. 315 */ 316 protected final boolean isUseXmlNamespaces() { 317 return useXmlNamespaces; 318 } 319 320 /** 321 * Configuration property: Collections should be serialized and parsed as loose collections. 322 * 323 * @see #RDF_looseCollections 324 * @return 325 * <jk>true</jk> if collections of resources are handled as loose collections of resources in RDF instead of 326 * resources that are children of an RDF collection (e.g. Sequence, Bag). 327 */ 328 protected final boolean isLooseCollections() { 329 return looseCollections; 330 } 331 332 /** 333 * Configuration property: Auto-detect namespace usage. 334 * 335 * @see #RDF_autoDetectNamespaces 336 * @return 337 * <jk>true</jk> if namespaces usage should be detected before serialization. 338 */ 339 protected final boolean isAutoDetectNamespaces() { 340 return autoDetectNamespaces; 341 } 342 343 /** 344 * Configuration property: Add <js>"_type"</js> properties when needed. 345 * 346 * @see #RDF_addBeanTypes 347 * @return 348 * <jk>true</jk> if <js>"_type"</js> properties will be added to beans if their type cannot be inferred 349 * through reflection. 350 */ 351 @Override 352 protected final boolean isAddBeanTypes() { 353 return addBeanTypes; 354 } 355 356 /** 357 * Configuration property: RDF language. 358 * 359 * @see #RDF_language 360 * @return 361 * The RDF language to use. 362 */ 363 protected final String getRdfLanguage() { 364 return rdfLanguage; 365 } 366 367 /** 368 * Configuration property: XML namespace for Juneau properties. 369 * 370 * @see #RDF_juneauNs 371 * @return 372 * The XML namespace to use for Juneau properties. 373 */ 374 protected final Namespace getJuneauNs() { 375 return juneauNs; 376 } 377 378 /** 379 * Configuration property: Default XML namespace for bean properties. 380 * 381 * @see #RDF_juneauBpNs 382 * @return 383 * The XML namespace to use for bean properties. 384 */ 385 protected final Namespace getJuneauBpNs() { 386 return juneauBpNs; 387 } 388 389 /** 390 * Configuration property: RDF format for representing collections and arrays. 391 * 392 * @see #RDF_collectionFormat 393 * @return 394 * RDF format for representing collections and arrays. 395 */ 396 protected final RdfCollectionFormat getCollectionFormat() { 397 return collectionFormat; 398 } 399 400 @Override /* Context */ 401 public ObjectMap asMap() { 402 return super.asMap() 403 .append("RdfSerializer", new ObjectMap() 404 .append("addLiteralTypes", addLiteralTypes) 405 .append("addRootProperty", addRootProperty) 406 .append("useXmlNamespaces", useXmlNamespaces) 407 .append("looseCollections", looseCollections) 408 .append("autoDetectNamespaces", autoDetectNamespaces) 409 .append("rdfLanguage", rdfLanguage) 410 .append("juneauNs", juneauNs) 411 .append("juneauBpNs", juneauBpNs) 412 .append("collectionFormat", collectionFormat) 413 .append("namespaces", namespaces) 414 .append("addBeanTypes", addBeanTypes) 415 ); 416 } 417 418 /** 419 * @deprecated Use {@link RdfXmlSerializer#DEFAULT} 420 */ 421 @Deprecated 422 public static final RdfSerializer DEFAULT_XML = new Xml(PropertyStore.DEFAULT); 423 424 /** 425 * @deprecated Use {@link RdfXmlAbbrevSerializer#DEFAULT} 426 */ 427 @Deprecated 428 public static final RdfSerializer DEFAULT_XMLABBREV = new XmlAbbrev(PropertyStore.DEFAULT); 429 430 /** 431 * @deprecated Use {@link TurtleSerializer#DEFAULT} 432 */ 433 @Deprecated 434 public static final RdfSerializer DEFAULT_TURTLE = new Turtle(PropertyStore.DEFAULT); 435 436 /** 437 * @deprecated Use {@link NTripleSerializer#DEFAULT} 438 */ 439 @Deprecated 440 public static final RdfSerializer DEFAULT_NTRIPLE = new NTriple(PropertyStore.DEFAULT); 441 442 /** 443 * @deprecated Use {@link N3Serializer#DEFAULT} 444 */ 445 @Deprecated 446 public static final RdfSerializer DEFAULT_N3 = new N3(PropertyStore.DEFAULT); 447 448 /** 449 * @deprecated Use {@link RdfXmlSerializer#DEFAULT} 450 */ 451 @Deprecated 452 @SuppressWarnings("javadoc") 453 public static class Xml extends RdfXmlSerializer { 454 public Xml(PropertyStore ps) { 455 super(ps); 456 } 457 } 458 459 /** 460 * @deprecated Use {@link RdfXmlAbbrevSerializer#DEFAULT} 461 */ 462 @Deprecated 463 @SuppressWarnings("javadoc") 464 public static class XmlAbbrev extends RdfXmlAbbrevSerializer { 465 public XmlAbbrev(PropertyStore ps) { 466 super(ps); 467 } 468 } 469 470 /** 471 * @deprecated Use {@link NTripleSerializer#DEFAULT} 472 */ 473 @Deprecated 474 @SuppressWarnings("javadoc") 475 public static class NTriple extends NTripleSerializer { 476 public NTriple(PropertyStore ps) { 477 super(ps); 478 } 479 } 480 481 /** 482 * @deprecated Use {@link TurtleSerializer#DEFAULT} 483 */ 484 @Deprecated 485 @SuppressWarnings("javadoc") 486 public static class Turtle extends TurtleSerializer { 487 public Turtle(PropertyStore ps) { 488 super(ps); 489 } 490 } 491 492 /** 493 * @deprecated Use {@link N3Serializer#DEFAULT} 494 */ 495 @Deprecated 496 @SuppressWarnings("javadoc") 497 public static class N3 extends N3Serializer { 498 public N3(PropertyStore ps) { 499 super(ps); 500 } 501 } 502 503 /** 504 * @deprecated Use {@link #RDF_addBeanTypes}. 505 */ 506 @Deprecated 507 public static final String RDF_addBeanTypeProperties = RDF_addBeanTypes; 508 509 /** 510 * @deprecated Use {@link #RdfSerializer(PropertyStore, String, String)}. 511 */ 512 @SuppressWarnings("javadoc") 513 @Deprecated 514 public RdfSerializer(PropertyStore ps, String produces, String...accept) { 515 this(ps, produces, StringUtils.join(accept, ",")); 516 } 517}