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.jena.Constants.*; 016import static org.apache.juneau.internal.CollectionUtils.*; 017 018import java.util.*; 019 020import org.apache.juneau.*; 021import org.apache.juneau.jena.annotation.*; 022import org.apache.juneau.serializer.*; 023import org.apache.juneau.xml.*; 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 RdfSerializer.Xml} - RDF/XML. 033 * <li>{@link RdfSerializer.XmlAbbrev} - RDF/XML-ABBREV. 034 * <li>{@link RdfSerializer.NTriple} - N-TRIPLE. 035 * <li>{@link RdfSerializer.Turtle} - TURTLE. 036 * <li>{@link RdfSerializer.N3} - N3. 037 * </ul> 038 * 039 * <h5 class='section'>See Also:</h5> 040 * <ul> 041 * <li class='link'><a class="doclink" href="package-summary.html#TOC">org.apache.juneau.jena > RDF Overview</a> 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.addBeanTypeProperties.b"</js> 062 * <li><b>Data type:</b> <code>Boolean</code> 063 * <li><b>Default:</b> <jk>false</jk> 064 * <li><b>Session-overridable:</b> <jk>true</jk> 065 * <li><b>Methods:</b> 066 * <ul> 067 * <li class='jm'>{@link RdfSerializerBuilder#addBeanTypeProperties(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_addBeanTypeProperties} setting and is 078 * provided to customize the behavior of specific serializers in a {@link SerializerGroup}. 079 */ 080 public static final String RDF_addBeanTypeProperties = PREFIX + "addBeanTypeProperties.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-overridable:</b> <jk>true</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-overridable:</b> <jk>true</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-overridable:</b> <jk>true</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-overridable:</b> <jk>true</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 //------------------------------------------------------------------------------------------------------------------- 180 // Predefined instances 181 //------------------------------------------------------------------------------------------------------------------- 182 183 /** Default RDF/XML serializer, all default settings.*/ 184 public static final RdfSerializer DEFAULT_XML = new Xml(PropertyStore.DEFAULT); 185 186 /** Default Abbreviated RDF/XML serializer, all default settings.*/ 187 public static final RdfSerializer DEFAULT_XMLABBREV = new XmlAbbrev(PropertyStore.DEFAULT); 188 189 /** Default Turtle serializer, all default settings.*/ 190 public static final RdfSerializer DEFAULT_TURTLE = new Turtle(PropertyStore.DEFAULT); 191 192 /** Default N-Triple serializer, all default settings.*/ 193 public static final RdfSerializer DEFAULT_NTRIPLE = new NTriple(PropertyStore.DEFAULT); 194 195 /** Default N3 serializer, all default settings.*/ 196 public static final RdfSerializer DEFAULT_N3 = new N3(PropertyStore.DEFAULT); 197 198 199 //------------------------------------------------------------------------------------------------------------------- 200 // Predefined subclasses 201 //------------------------------------------------------------------------------------------------------------------- 202 203 /** Produces RDF/XML output */ 204 public static class Xml extends RdfSerializer { 205 206 /** 207 * Constructor. 208 * 209 * @param ps The property store containing all the settings for this object. 210 */ 211 public Xml(PropertyStore ps) { 212 super( 213 ps.builder() 214 .set(RDF_language, LANG_RDF_XML) 215 .build(), 216 "text/xml+rdf" 217 ); 218 } 219 } 220 221 /** Produces Abbreviated RDF/XML output */ 222 public static class XmlAbbrev extends RdfSerializer { 223 224 /** 225 * Constructor. 226 * 227 * @param ps The property store containing all the settings for this object. 228 */ 229 public XmlAbbrev(PropertyStore ps) { 230 super( 231 ps.builder() 232 .set(RDF_language, LANG_RDF_XML_ABBREV) 233 .build(), 234 "text/xml+rdf", 235 "text/xml+rdf+abbrev" 236 ); 237 } 238 } 239 240 /** Produces N-Triple output */ 241 public static class NTriple extends RdfSerializer { 242 243 /** 244 * Constructor. 245 * 246 * @param ps The property store containing all the settings for this object. 247 */ 248 public NTriple(PropertyStore ps) { 249 super( 250 ps.builder() 251 .set(RDF_language, LANG_NTRIPLE) 252 .build(), 253 "text/n-triple" 254 ); 255 } 256 } 257 258 /** Produces Turtle output */ 259 public static class Turtle extends RdfSerializer { 260 261 /** 262 * Constructor. 263 * 264 * @param ps The property store containing all the settings for this object. 265 */ 266 public Turtle(PropertyStore ps) { 267 super( 268 ps.builder() 269 .set(RDF_language, LANG_TURTLE) 270 .build(), 271 "text/turtle" 272 ); 273 } 274 } 275 276 /** Produces N3 output */ 277 public static class N3 extends RdfSerializer { 278 279 /** 280 * Constructor. 281 * 282 * @param ps The property store containing all the settings for this object. 283 */ 284 public N3(PropertyStore ps) { 285 super( 286 ps.builder() 287 .set(RDF_language, LANG_N3) 288 .build(), 289 "text/n3" 290 ); 291 } 292 } 293 294 295 //------------------------------------------------------------------------------------------------------------------- 296 // Instance 297 //------------------------------------------------------------------------------------------------------------------- 298 299 final boolean 300 addLiteralTypes, 301 addRootProperty, 302 useXmlNamespaces, 303 looseCollections, 304 autoDetectNamespaces, 305 addBeanTypeProperties; 306 final String rdfLanguage; 307 final Namespace juneauNs; 308 final Namespace juneauBpNs; 309 final RdfCollectionFormat collectionFormat; 310 final Map<String,Object> jenaSettings; 311 final Namespace[] namespaces; 312 313 /** 314 * Constructor. 315 * 316 * @param ps 317 * The property store containing all the settings for this object. 318 * @param produces 319 * The media type that this serializer produces. 320 * @param accept 321 * The accept media types that the serializer can handle. 322 * <p> 323 * Can contain meta-characters per the <code>media-type</code> specification of 324 * <a class="doclink" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.1">RFC2616/14.1</a> 325 * <p> 326 * If empty, then assumes the only media type supported is <code>produces</code>. 327 * <p> 328 * For example, if this serializer produces <js>"application/json"</js> but should handle media types of 329 * <js>"application/json"</js> and <js>"text/json"</js>, then the arguments should be: 330 * <p class='bcode'> 331 * <jk>super</jk>(ps, <js>"application/json"</js>, <js>"application/json"</js>, <js>"text/json"</js>); 332 * </p> 333 * <br>...or... 334 * <p class='bcode'> 335 * <jk>super</jk>(ps, <js>"application/json"</js>, <js>"*​/json"</js>); 336 * </p> 337 */ 338 public RdfSerializer(PropertyStore ps, String produces, String...accept) { 339 super(ps, produces, accept); 340 addLiteralTypes = getBooleanProperty(RDF_addLiteralTypes, false); 341 addRootProperty = getBooleanProperty(RDF_addRootProperty, false); 342 useXmlNamespaces = getBooleanProperty(RDF_useXmlNamespaces, true); 343 looseCollections = getBooleanProperty(RDF_looseCollections, false); 344 autoDetectNamespaces = getBooleanProperty(RDF_autoDetectNamespaces, true); 345 rdfLanguage = getStringProperty(RDF_language, "RDF/XML-ABBREV"); 346 juneauNs = getProperty(RDF_juneauNs, Namespace.class, DEFAULT_JUNEAU_NS); 347 juneauBpNs = getProperty(RDF_juneauBpNs, Namespace.class, DEFAULT_JUNEAUBP_NS); 348 collectionFormat = getProperty(RDF_collectionFormat, RdfCollectionFormat.class, RdfCollectionFormat.DEFAULT); 349 namespaces = getProperty(RDF_namespaces, Namespace[].class, new Namespace[0]); 350 addBeanTypeProperties = getBooleanProperty(RDF_addBeanTypeProperties, getBooleanProperty(SERIALIZER_addBeanTypeProperties, true)); 351 352 Map<String,Object> m = new LinkedHashMap<>(); 353 for (String k : getPropertyKeys("RdfCommon")) 354 if (k.startsWith("jena.")) 355 m.put(k.substring(5), getProperty(k)); 356 jenaSettings = unmodifiableMap(m); 357 } 358 359 /** 360 * Constructor. 361 * 362 * @param ps 363 * The property store containing all the settings for this object. 364 */ 365 public RdfSerializer(PropertyStore ps) { 366 this(ps, "text/xml+rdf"); 367 } 368 369 370 @Override /* Context */ 371 public RdfSerializerBuilder builder() { 372 return new RdfSerializerBuilder(getPropertyStore()); 373 } 374 375 /** 376 * Instantiates a new clean-slate {@link RdfSerializerBuilder} object. 377 * 378 * <p> 379 * This is equivalent to simply calling <code><jk>new</jk> RdfSerializerBuilder()</code>. 380 * 381 * <p> 382 * Note that this method creates a builder initialized to all default settings, whereas {@link #builder()} copies 383 * the settings of the object called on. 384 * 385 * @return A new {@link RdfSerializerBuilder} object. 386 */ 387 public static RdfSerializerBuilder create() { 388 return new RdfSerializerBuilder(); 389 } 390 391 @Override /* Serializer */ 392 public WriterSerializerSession createSession(SerializerSessionArgs args) { 393 return new RdfSerializerSession(this, args); 394 } 395 396 @Override /* Context */ 397 public ObjectMap asMap() { 398 return super.asMap() 399 .append("RdfSerializer", new ObjectMap() 400 .append("addLiteralTypes", addLiteralTypes) 401 .append("addRootProperty", addRootProperty) 402 .append("useXmlNamespaces", useXmlNamespaces) 403 .append("looseCollections", looseCollections) 404 .append("autoDetectNamespaces", autoDetectNamespaces) 405 .append("rdfLanguage", rdfLanguage) 406 .append("juneauNs", juneauNs) 407 .append("juneauBpNs", juneauBpNs) 408 .append("collectionFormat", collectionFormat) 409 .append("namespaces", namespaces) 410 .append("addBeanTypeProperties", addBeanTypeProperties) 411 ); 412 } 413}