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.annotation.*; 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 * <ul class='seealso'> 041 * <li class='link'>{@doc juneau-marshall-rdf} 042 * </ul> 043 */ 044@ConfigurableContext(prefixes={RdfCommon.PREFIX,RdfSerializer.PREFIX}) 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 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> <c>Boolean</c> 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-<c>String</c> 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> <c>Boolean</c> 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> <c>Boolean</c> 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 <c>http://www.apache.org/juneau/root</c> 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> <c>Boolean</c> 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> <c>List<String></c> (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 * Configuration property: Reuse XML namespaces when RDF namespaces not specified. 181 * 182 * <h5 class='section'>Property:</h5> 183 * <ul> 184 * <li><b>Name:</b> <js>"Rdf.useXmlNamespaces.b"</js> 185 * <li><b>Data type:</b> <c>Boolean</c> 186 * <li><b>Default:</b> <jk>true</jk> 187 * <li><b>Methods:</b> 188 * <ul> 189 * <li class='jm'>{@link RdfSerializerBuilder#useXmlNamespaces(boolean)} 190 * </ul> 191 * </ul> 192 * 193 * <h5 class='section'>Description:</h5> 194 * <p> 195 * When specified, namespaces defined using {@link XmlNs @XmlNs} and {@link Xml @Xml} will be inherited by the RDF serializers. 196 * <br>Otherwise, namespaces will be defined using {@link RdfNs @RdfNs} and {@link Rdf @Rdf}. 197 */ 198 public static final String RDF_useXmlNamespaces = PREFIX + ".useXmlNamespaces.b"; 199 200 //------------------------------------------------------------------------------------------------------------------- 201 // Instance 202 //------------------------------------------------------------------------------------------------------------------- 203 204 private final boolean 205 addLiteralTypes, 206 addRootProperty, 207 useXmlNamespaces, 208 looseCollections, 209 autoDetectNamespaces, 210 addBeanTypes; 211 private final String rdfLanguage; 212 private final Namespace juneauNs; 213 private final Namespace juneauBpNs; 214 private final RdfCollectionFormat collectionFormat; 215 final Map<String,Object> jenaProperties; 216 final Namespace[] namespaces; 217 218 /** 219 * Constructor. 220 * 221 * @param ps 222 * The property store containing all the settings for this object. 223 * @param produces 224 * The media type that this serializer produces. 225 * @param accept 226 * The accept media types that the serializer can handle. 227 * <p> 228 * Can contain meta-characters per the <c>media-type</c> specification of {@doc RFC2616.section14.1} 229 * <p> 230 * If empty, then assumes the only media type supported is <c>produces</c>. 231 * <p> 232 * For example, if this serializer produces <js>"application/json"</js> but should handle media types of 233 * <js>"application/json"</js> and <js>"text/json"</js>, then the arguments should be: 234 * <p class='bcode w800'> 235 * <jk>super</jk>(ps, <js>"application/json"</js>, <js>"application/json,text/json"</js>); 236 * </p> 237 * <br>...or... 238 * <p class='bcode w800'> 239 * <jk>super</jk>(ps, <js>"application/json"</js>, <js>"*​/json"</js>); 240 * </p> 241 * <p> 242 * The accept value can also contain q-values. 243 */ 244 public RdfSerializer(PropertyStore ps, String produces, String accept) { 245 super(ps, produces, accept); 246 addLiteralTypes = getBooleanProperty(RDF_addLiteralTypes, false); 247 addRootProperty = getBooleanProperty(RDF_addRootProperty, false); 248 useXmlNamespaces = getBooleanProperty(RDF_useXmlNamespaces, true); 249 looseCollections = getBooleanProperty(RDF_looseCollections, false); 250 autoDetectNamespaces = getBooleanProperty(RDF_autoDetectNamespaces, true); 251 rdfLanguage = getStringProperty(RDF_language, "RDF/XML-ABBREV"); 252 juneauNs = getProperty(RDF_juneauNs, Namespace.class, DEFAULT_JUNEAU_NS); 253 juneauBpNs = getProperty(RDF_juneauBpNs, Namespace.class, DEFAULT_JUNEAUBP_NS); 254 collectionFormat = getProperty(RDF_collectionFormat, RdfCollectionFormat.class, RdfCollectionFormat.DEFAULT); 255 namespaces = getProperty(RDF_namespaces, Namespace[].class, new Namespace[0]); 256 addBeanTypes = getBooleanProperty(RDF_addBeanTypes, getBooleanProperty(SERIALIZER_addBeanTypes, false)); 257 258 Map<String,Object> m = new TreeMap<>(); 259 for (String k : getPropertyKeys("RdfCommon")) 260 if (k.startsWith("jena.")) 261 m.put(k.substring(5), getProperty("RdfCommon." + k)); 262 jenaProperties = unmodifiableMap(m); 263 } 264 265 /** 266 * Constructor. 267 * 268 * @param ps 269 * The property store containing all the settings for this object. 270 */ 271 public RdfSerializer(PropertyStore ps) { 272 this(ps, "text/xml+rdf", (String)null); 273 } 274 275 276 @Override /* Context */ 277 public RdfSerializerBuilder builder() { 278 return new RdfSerializerBuilder(getPropertyStore()); 279 } 280 281 /** 282 * Instantiates a new clean-slate {@link RdfSerializerBuilder} object. 283 * 284 * <p> 285 * This is equivalent to simply calling <code><jk>new</jk> RdfSerializerBuilder()</code>. 286 * 287 * <p> 288 * Note that this method creates a builder initialized to all default settings, whereas {@link #builder()} copies 289 * the settings of the object called on. 290 * 291 * @return A new {@link RdfSerializerBuilder} object. 292 */ 293 public static RdfSerializerBuilder create() { 294 return new RdfSerializerBuilder(); 295 } 296 297 @Override /* Context */ 298 public RdfSerializerSession createSession() { 299 return createSession(createDefaultSessionArgs()); 300 } 301 302 @Override /* Serializer */ 303 public RdfSerializerSession createSession(SerializerSessionArgs args) { 304 return new RdfSerializerSession(this, args); 305 } 306 307 //----------------------------------------------------------------------------------------------------------------- 308 // Common properties 309 //----------------------------------------------------------------------------------------------------------------- 310 311 /** 312 * Configuration property: RDF format for representing collections and arrays. 313 * 314 * @see #RDF_collectionFormat 315 * @return 316 * RDF format for representing collections and arrays. 317 */ 318 protected final RdfCollectionFormat getCollectionFormat() { 319 return collectionFormat; 320 } 321 322 /** 323 * Configuration property: Default XML namespace for bean properties. 324 * 325 * @see #RDF_juneauBpNs 326 * @return 327 * The XML namespace to use for bean properties. 328 */ 329 protected final Namespace getJuneauBpNs() { 330 return juneauBpNs; 331 } 332 333 /** 334 * Configuration property: XML namespace for Juneau properties. 335 * 336 * @see #RDF_juneauNs 337 * @return 338 * The XML namespace to use for Juneau properties. 339 */ 340 protected final Namespace getJuneauNs() { 341 return juneauNs; 342 } 343 344 /** 345 * Configuration property: RDF language. 346 * 347 * @see #RDF_language 348 * @return 349 * The RDF language to use. 350 */ 351 protected final String getLanguage() { 352 return rdfLanguage; 353 } 354 355 /** 356 * Configuration property: Collections should be serialized and parsed as loose collections. 357 * 358 * @see #RDF_looseCollections 359 * @return 360 * <jk>true</jk> if collections of resources are handled as loose collections of resources in RDF instead of 361 * resources that are children of an RDF collection (e.g. Sequence, Bag). 362 */ 363 protected final boolean isLooseCollections() { 364 return looseCollections; 365 } 366 367 //----------------------------------------------------------------------------------------------------------------- 368 // Jena properties 369 //----------------------------------------------------------------------------------------------------------------- 370 371 /** 372 * Configuration property: All Jena-related configuration properties. 373 * 374 * @return 375 * A map of all Jena-related configuration properties. 376 */ 377 protected final Map<String,Object> getJenaProperties() { 378 return jenaProperties; 379 } 380 381 //----------------------------------------------------------------------------------------------------------------- 382 // Properties 383 //----------------------------------------------------------------------------------------------------------------- 384 385 /** 386 * Configuration property: Add <js>"_type"</js> properties when needed. 387 * 388 * @see #RDF_addBeanTypes 389 * @return 390 * <jk>true</jk> if <js>"_type"</js> properties will be added to beans if their type cannot be inferred 391 * through reflection. 392 */ 393 @Override 394 protected final boolean isAddBeanTypes() { 395 return addBeanTypes; 396 } 397 398 /** 399 * Configuration property: Add XSI data types to non-<c>String</c> literals. 400 * 401 * @see #RDF_addLiteralTypes 402 * @return 403 * <jk>true</jk> if XSI data types should be added to string literals. 404 */ 405 protected final boolean isAddLiteralTypes() { 406 return addLiteralTypes; 407 } 408 409 /** 410 * Configuration property: Add RDF root identifier property to root node. 411 * 412 * @see RdfSerializer#RDF_addRootProperty 413 * @return 414 * <jk>true</jk> if RDF property <c>http://www.apache.org/juneau/root</c> is added with a value of <js>"true"</js> 415 * to identify the root node in the graph. 416 */ 417 protected final boolean isAddRootProp() { 418 return addRootProperty; 419 } 420 421 /** 422 * Configuration property: Auto-detect namespace usage. 423 * 424 * @see #RDF_autoDetectNamespaces 425 * @return 426 * <jk>true</jk> if namespaces usage should be detected before serialization. 427 */ 428 protected final boolean isAutoDetectNamespaces() { 429 return autoDetectNamespaces; 430 } 431 432 /** 433 * Configuration property: Default namespaces. 434 * 435 * @see #RDF_namespaces 436 * @return 437 * The default list of namespaces associated with this serializer. 438 */ 439 public Namespace[] getNamespaces() { 440 return namespaces; 441 } 442 443 /** 444 * Configuration property: Reuse XML namespaces when RDF namespaces not specified. 445 * 446 * @see #RDF_useXmlNamespaces 447 * @return 448 * <jk>true</jk> if namespaces defined using {@link XmlNs @XmlNs} and {@link Xml @Xml} will be inherited by the RDF serializers. 449 * <br>Otherwise, namespaces will be defined using {@link RdfNs @RdfNs} and {@link Rdf @Rdf}. 450 */ 451 protected final boolean isUseXmlNamespaces() { 452 return useXmlNamespaces; 453 } 454 455 //----------------------------------------------------------------------------------------------------------------- 456 // Other methods 457 //----------------------------------------------------------------------------------------------------------------- 458 459 @Override /* Context */ 460 public ObjectMap toMap() { 461 return super.toMap() 462 .append("RdfSerializer", new DefaultFilteringObjectMap() 463 .append("addLiteralTypes", addLiteralTypes) 464 .append("addRootProperty", addRootProperty) 465 .append("useXmlNamespaces", useXmlNamespaces) 466 .append("looseCollections", looseCollections) 467 .append("autoDetectNamespaces", autoDetectNamespaces) 468 .append("rdfLanguage", rdfLanguage) 469 .append("juneauNs", juneauNs) 470 .append("juneauBpNs", juneauBpNs) 471 .append("collectionFormat", collectionFormat) 472 .append("namespaces", namespaces) 473 .append("addBeanTypes", addBeanTypes) 474 ); 475 } 476}