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&lt;String&gt;</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>"*&#8203;/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}