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.jena.annotation.*; 021import org.apache.juneau.serializer.*; 022import org.apache.juneau.xml.*; 023import org.apache.juneau.xml.annotation.*; 024 025/** 026 * Serializes POJOs to RDF. 027 * 028 * <h5 class='topic'>Behavior-specific subclasses</h5> 029 * 030 * The following direct subclasses are provided for language-specific serializers: 031 * <ul> 032 * <li>{@link RdfXmlSerializer} - RDF/XML. 033 * <li>{@link RdfXmlAbbrevSerializer} - RDF/XML-ABBREV. 034 * <li>{@link NTripleSerializer} - N-TRIPLE. 035 * <li>{@link TurtleSerializer} - TURTLE. 036 * <li>{@link N3Serializer} - N3. 037 * </ul> 038 * 039 * <h5 class='section'>See Also:</h5> 040 * <ul> 041 * <li class='link'>{@doc juneau-marshall-rdf} 042 * </ul> 043 */ 044public class RdfSerializer extends WriterSerializer implements RdfCommon { 045 046 private static final Namespace 047 DEFAULT_JUNEAU_NS = Namespace.create("j", "http://www.apache.org/juneau/"), 048 DEFAULT_JUNEAUBP_NS = Namespace.create("jp", "http://www.apache.org/juneaubp/"); 049 050 //------------------------------------------------------------------------------------------------------------------- 051 // Configurable properties 052 //------------------------------------------------------------------------------------------------------------------- 053 054 private static final String PREFIX = "RdfSerializer."; 055 056 /** 057 * Configuration property: Add <js>"_type"</js> properties when needed. 058 * 059 * <h5 class='section'>Property:</h5> 060 * <ul> 061 * <li><b>Name:</b> <js>"RdfSerializer.addBeanTypes.b"</js> 062 * <li><b>Data type:</b> <code>Boolean</code> 063 * <li><b>Default:</b> <jk>false</jk> 064 * <li><b>Session property:</b> <jk>false</jk> 065 * <li><b>Methods:</b> 066 * <ul> 067 * <li class='jm'>{@link RdfSerializerBuilder#addBeanTypes(boolean)} 068 * </ul> 069 * </ul> 070 * 071 * <h5 class='section'>Description:</h5> 072 * <p> 073 * If <jk>true</jk>, then <js>"_type"</js> properties will be added to beans if their type cannot be inferred 074 * through reflection. 075 * 076 * <p> 077 * When present, this value overrides the {@link #SERIALIZER_addBeanTypes} setting and is 078 * provided to customize the behavior of specific serializers in a {@link SerializerGroup}. 079 */ 080 public static final String RDF_addBeanTypes = PREFIX + "addBeanTypes.b"; 081 082 /** 083 * Configuration property: Add XSI data types to non-<code>String</code> literals. 084 * 085 * <h5 class='section'>Property:</h5> 086 * <ul> 087 * <li><b>Name:</b> <js>"RdfSerializer.addLiteralTypes.b"</js> 088 * <li><b>Data type:</b> <code>Boolean</code> 089 * <li><b>Default:</b> <jk>false</jk> 090 * <li><b>Session property:</b> <jk>false</jk> 091 * <li><b>Methods:</b> 092 * <ul> 093 * <li class='jm'>{@link RdfSerializerBuilder#addLiteralTypes(boolean)} 094 * <li class='jm'>{@link RdfSerializerBuilder#addLiteralTypes()} 095 * </ul> 096 * </ul> 097 */ 098 public static final String RDF_addLiteralTypes = PREFIX + "addLiteralTypes.b"; 099 100 /** 101 * Configuration property: Add RDF root identifier property to root node. 102 * 103 * <h5 class='section'>Property:</h5> 104 * <ul> 105 * <li><b>Name:</b> <js>"RdfSerializer.addRootProperty.b"</js> 106 * <li><b>Data type:</b> <code>Boolean</code> 107 * <li><b>Default:</b> <jk>false</jk> 108 * <li><b>Session property:</b> <jk>false</jk> 109 * <li><b>Methods:</b> 110 * <ul> 111 * <li class='jm'>{@link RdfSerializerBuilder#addRootProperty(boolean)} 112 * <li class='jm'>{@link RdfSerializerBuilder#addRootProperty()} 113 * </ul> 114 * </ul> 115 * 116 * <h5 class='section'>Description:</h5> 117 * <p> 118 * When enabled an RDF property <code>http://www.apache.org/juneau/root</code> is added with a value of <js>"true"</js> 119 * to identify the root node in the graph. 120 * <br>This helps locate the root node during parsing. 121 * 122 * <p> 123 * If disabled, the parser has to search through the model to find any resources without incoming predicates to 124 * identify root notes, which can introduce a considerable performance degradation. 125 */ 126 public static final String RDF_addRootProperty = PREFIX + "addRootProperty.b"; 127 128 /** 129 * Configuration property: Auto-detect namespace usage. 130 * 131 * <h5 class='section'>Property:</h5> 132 * <ul> 133 * <li><b>Name:</b> <js>"RdfSerializer.autoDetectNamespaces.b"</js> 134 * <li><b>Data type:</b> <code>Boolean</code> 135 * <li><b>Default:</b> <jk>true</jk> 136 * <li><b>Session property:</b> <jk>false</jk> 137 * <li><b>Methods:</b> 138 * <ul> 139 * <li class='jm'>{@link RdfSerializerBuilder#autoDetectNamespaces(boolean)} 140 * </ul> 141 * </ul> 142 * 143 * <h5 class='section'>Description:</h5> 144 * <p> 145 * Detect namespace usage before serialization. 146 * 147 * <p> 148 * If enabled, then the data structure will first be crawled looking for namespaces that will be encountered before 149 * the root element is serialized. 150 */ 151 public static final String RDF_autoDetectNamespaces = PREFIX + "autoDetectNamespaces.b"; 152 153 /** 154 * Configuration property: Default namespaces. 155 * 156 * <h5 class='section'>Property:</h5> 157 * <ul> 158 * <li><b>Name:</b> <js>"RdfSerializer.namespaces.ls"</js> 159 * <li><b>Data type:</b> <code>List<String></code> (serialized {@link Namespace} objects) 160 * <li><b>Default:</b> empty list 161 * <li><b>Session property:</b> <jk>false</jk> 162 * <li><b>Annotations:</b> 163 * <ul> 164 * <li class='ja'>{@link Rdf#namespace()} 165 * </ul> 166 * <li><b>Methods:</b> 167 * <ul> 168 * <li class='jm'>{@link RdfSerializerBuilder#namespaces(Namespace...)} 169 * </ul> 170 * </ul> 171 * 172 * <h5 class='section'>Description:</h5> 173 * <p> 174 * The default list of namespaces associated with this serializer. 175 */ 176 public static final String RDF_namespaces = PREFIX + "namespaces.ls"; 177 178 //------------------------------------------------------------------------------------------------------------------- 179 // Instance 180 //------------------------------------------------------------------------------------------------------------------- 181 182 private final boolean 183 addLiteralTypes, 184 addRootProperty, 185 useXmlNamespaces, 186 looseCollections, 187 autoDetectNamespaces, 188 addBeanTypes; 189 private final String rdfLanguage; 190 private final Namespace juneauNs; 191 private final Namespace juneauBpNs; 192 private final RdfCollectionFormat collectionFormat; 193 final Map<String,Object> jenaSettings; 194 final Namespace[] namespaces; 195 196 /** 197 * Constructor. 198 * 199 * @param ps 200 * The property store containing all the settings for this object. 201 * @param produces 202 * The media type that this serializer produces. 203 * @param accept 204 * The accept media types that the serializer can handle. 205 * <p> 206 * Can contain meta-characters per the <code>media-type</code> specification of {@doc RFC2616.section14.1} 207 * <p> 208 * If empty, then assumes the only media type supported is <code>produces</code>. 209 * <p> 210 * For example, if this serializer produces <js>"application/json"</js> but should handle media types of 211 * <js>"application/json"</js> and <js>"text/json"</js>, then the arguments should be: 212 * <p class='bcode w800'> 213 * <jk>super</jk>(ps, <js>"application/json"</js>, <js>"application/json,text/json"</js>); 214 * </p> 215 * <br>...or... 216 * <p class='bcode w800'> 217 * <jk>super</jk>(ps, <js>"application/json"</js>, <js>"*​/json"</js>); 218 * </p> 219 * <p> 220 * The accept value can also contain q-values. 221 */ 222 public RdfSerializer(PropertyStore ps, String produces, String accept) { 223 super(ps, produces, accept); 224 addLiteralTypes = getBooleanProperty(RDF_addLiteralTypes, false); 225 addRootProperty = getBooleanProperty(RDF_addRootProperty, false); 226 useXmlNamespaces = getBooleanProperty(RDF_useXmlNamespaces, true); 227 looseCollections = getBooleanProperty(RDF_looseCollections, false); 228 autoDetectNamespaces = getBooleanProperty(RDF_autoDetectNamespaces, true); 229 rdfLanguage = getStringProperty(RDF_language, "RDF/XML-ABBREV"); 230 juneauNs = getProperty(RDF_juneauNs, Namespace.class, DEFAULT_JUNEAU_NS); 231 juneauBpNs = getProperty(RDF_juneauBpNs, Namespace.class, DEFAULT_JUNEAUBP_NS); 232 collectionFormat = getProperty(RDF_collectionFormat, RdfCollectionFormat.class, RdfCollectionFormat.DEFAULT); 233 namespaces = getProperty(RDF_namespaces, Namespace[].class, new Namespace[0]); 234 addBeanTypes = getBooleanProperty(RDF_addBeanTypes, getBooleanProperty(SERIALIZER_addBeanTypes, false)); 235 236 Map<String,Object> m = new LinkedHashMap<>(); 237 for (String k : getPropertyKeys("RdfCommon")) 238 if (k.startsWith("jena.")) 239 m.put(k.substring(5), getProperty(k)); 240 jenaSettings = unmodifiableMap(m); 241 } 242 243 /** 244 * Constructor. 245 * 246 * @param ps 247 * The property store containing all the settings for this object. 248 */ 249 public RdfSerializer(PropertyStore ps) { 250 this(ps, "text/xml+rdf", null); 251 } 252 253 254 @Override /* Context */ 255 public RdfSerializerBuilder builder() { 256 return new RdfSerializerBuilder(getPropertyStore()); 257 } 258 259 /** 260 * Instantiates a new clean-slate {@link RdfSerializerBuilder} object. 261 * 262 * <p> 263 * This is equivalent to simply calling <code><jk>new</jk> RdfSerializerBuilder()</code>. 264 * 265 * <p> 266 * Note that this method creates a builder initialized to all default settings, whereas {@link #builder()} copies 267 * the settings of the object called on. 268 * 269 * @return A new {@link RdfSerializerBuilder} object. 270 */ 271 public static RdfSerializerBuilder create() { 272 return new RdfSerializerBuilder(); 273 } 274 275 @Override /* Serializer */ 276 public WriterSerializerSession createSession(SerializerSessionArgs args) { 277 return new RdfSerializerSession(this, args); 278 } 279 280 //----------------------------------------------------------------------------------------------------------------- 281 // Properties 282 //----------------------------------------------------------------------------------------------------------------- 283 284 /** 285 * Configuration property: Add XSI data types to non-<code>String</code> literals. 286 * 287 * @see #RDF_addLiteralTypes 288 * @return 289 * <jk>true</jk> if XSI data types should be added to string literals. 290 */ 291 protected final boolean isAddLiteralTypes() { 292 return addLiteralTypes; 293 } 294 295 /** 296 * Configuration property: Add RDF root identifier property to root node. 297 * 298 * @see #RDF_addRootProperty 299 * @return 300 * <jk>true</jk> if RDF property <code>http://www.apache.org/juneau/root</code> is added with a value of <js>"true"</js> 301 * to identify the root node in the graph. 302 */ 303 protected final boolean isAddRootProp() { 304 return addRootProperty; 305 } 306 307 /** 308 * Configuration property: Reuse XML namespaces when RDF namespaces not specified. 309 * 310 * @see #RDF_useXmlNamespaces 311 * @return 312 * <jk>true</jk> if namespaces defined using {@link XmlNs @XmlNs} and {@link Xml @Xml} will be inherited by the RDF serializers. 313 * <br>Otherwise, namespaces will be defined using {@link RdfNs @RdfNs} and {@link Rdf @Rdf}. 314 */ 315 protected final boolean isUseXmlNamespaces() { 316 return useXmlNamespaces; 317 } 318 319 /** 320 * Configuration property: Collections should be serialized and parsed as loose collections. 321 * 322 * @see #RDF_looseCollections 323 * @return 324 * <jk>true</jk> if collections of resources are handled as loose collections of resources in RDF instead of 325 * resources that are children of an RDF collection (e.g. Sequence, Bag). 326 */ 327 protected final boolean isLooseCollections() { 328 return looseCollections; 329 } 330 331 /** 332 * Configuration property: Auto-detect namespace usage. 333 * 334 * @see #RDF_autoDetectNamespaces 335 * @return 336 * <jk>true</jk> if namespaces usage should be detected before serialization. 337 */ 338 protected final boolean isAutoDetectNamespaces() { 339 return autoDetectNamespaces; 340 } 341 342 /** 343 * Configuration property: Add <js>"_type"</js> properties when needed. 344 * 345 * @see #RDF_addBeanTypes 346 * @return 347 * <jk>true</jk> if <js>"_type"</js> properties will be added to beans if their type cannot be inferred 348 * through reflection. 349 */ 350 @Override 351 protected final boolean isAddBeanTypes() { 352 return addBeanTypes; 353 } 354 355 /** 356 * Configuration property: RDF language. 357 * 358 * @see #RDF_language 359 * @return 360 * The RDF language to use. 361 */ 362 protected final String getRdfLanguage() { 363 return rdfLanguage; 364 } 365 366 /** 367 * Configuration property: XML namespace for Juneau properties. 368 * 369 * @see #RDF_juneauNs 370 * @return 371 * The XML namespace to use for Juneau properties. 372 */ 373 protected final Namespace getJuneauNs() { 374 return juneauNs; 375 } 376 377 /** 378 * Configuration property: Default XML namespace for bean properties. 379 * 380 * @see #RDF_juneauBpNs 381 * @return 382 * The XML namespace to use for bean properties. 383 */ 384 protected final Namespace getJuneauBpNs() { 385 return juneauBpNs; 386 } 387 388 /** 389 * Configuration property: RDF format for representing collections and arrays. 390 * 391 * @see #RDF_collectionFormat 392 * @return 393 * RDF format for representing collections and arrays. 394 */ 395 protected final RdfCollectionFormat getCollectionFormat() { 396 return collectionFormat; 397 } 398 399 @Override /* Context */ 400 public ObjectMap asMap() { 401 return super.asMap() 402 .append("RdfSerializer", new ObjectMap() 403 .append("addLiteralTypes", addLiteralTypes) 404 .append("addRootProperty", addRootProperty) 405 .append("useXmlNamespaces", useXmlNamespaces) 406 .append("looseCollections", looseCollections) 407 .append("autoDetectNamespaces", autoDetectNamespaces) 408 .append("rdfLanguage", rdfLanguage) 409 .append("juneauNs", juneauNs) 410 .append("juneauBpNs", juneauBpNs) 411 .append("collectionFormat", collectionFormat) 412 .append("namespaces", namespaces) 413 .append("addBeanTypes", addBeanTypes) 414 ); 415 } 416}