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