001/*
002 * Licensed to the Apache Software Foundation (ASF) under one or more
003 * contributor license agreements.  See the NOTICE file distributed with
004 * this work for additional information regarding copyright ownership.
005 * The ASF licenses this file to You under the Apache License, Version 2.0
006 * (the "License"); you may not use this file except in compliance with
007 * the License.  You may obtain a copy of the License at
008 *
009 *      http://www.apache.org/licenses/LICENSE-2.0
010 *
011 * Unless required by applicable law or agreed to in writing, software
012 * distributed under the License is distributed on an "AS IS" BASIS,
013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014 * See the License for the specific language governing permissions and
015 * limitations under the License. */
016package org.apache.juneau.marshall.jena;
017
018
019import org.apache.juneau.commons.bean.*;
020import org.apache.juneau.commons.collections.*;
021import org.apache.juneau.marshall.*;
022import org.apache.juneau.marshall.serializer.*;
023import org.apache.juneau.marshall.xml.*;
024import static org.apache.juneau.commons.utils.Shorts.*;
025
026/**
027 * Stream-based RDF serializer for binary formats (RDF/THRIFT, RDF/PROTO).
028 *
029 * <p>
030 * Extends {@link OutputStreamSerializer} and delegates RDF metadata to an internal {@link RdfSerializer}.
031 *
032 * <h5 class='section'>Notes:</h5><ul>
033 *    <li class='note'>This class is thread safe and reusable.
034 * </ul>
035 *
036 * <h5 class='section'>See Also:</h5><ul>
037 *    <li class='link'>{doc jmr.RdfDetails}
038 * </ul>
039 */
040@SuppressWarnings({
041   "java:S110", // Deep inheritance inherent to the RDF serializer hierarchy.
042   "java:S1192" // Duplicated literals (argument/property names) read more clearly inline than as constants
043})
044public class RdfStreamSerializer extends OutputStreamSerializer implements RdfMetaProvider {
045
046   /**
047    * Builder class.
048    */
049   @SuppressWarnings({
050      "java:S119" // 'SELF' (CRTP self-type) is intentional and clearer than a single-letter name.
051   })
052   public abstract static class Builder<SELF extends Builder<SELF>> extends OutputStreamSerializer.Builder<SELF> {
053
054      private static final Cache<HashKey,RdfStreamSerializer> CACHE = Cache.of(HashKey.class, RdfStreamSerializer.class).build();
055
056      private String language;
057      private boolean addBeanTypes;
058      private RdfSerializer rdfSerializer;
059
060      /**
061       * Constructor, default settings.
062       */
063      protected Builder() {
064         language = Constants.LANG_RDFTHRIFT;
065      }
066
067      /**
068       * Copy constructor.
069       *
070       * @param copyFrom The builder to copy from.
071       *    <br>Cannot be <jk>null</jk>.
072       */
073      protected Builder(Builder<?> copyFrom) {
074         super(reqnn("copyFrom", copyFrom));
075         language = copyFrom.language;
076         addBeanTypes = copyFrom.addBeanTypes;
077         rdfSerializer = copyFrom.rdfSerializer;
078      }
079
080      /**
081       * Copy constructor.
082       *
083       * @param copyFrom The bean to copy from.
084       *    <br>Cannot be <jk>null</jk>.
085       */
086      protected Builder(RdfStreamSerializer copyFrom) {
087         super(reqnn("copyFrom", copyFrom));
088         language = copyFrom.language;
089         addBeanTypes = copyFrom.isAddBeanTypes();
090         rdfSerializer = copyFrom.rdfSerializer;
091      }
092
093      /**
094       * RDF language (e.g. {@link Constants#LANG_RDFTHRIFT}, {@link Constants#LANG_RDFPROTO}).
095       *
096       * @param value The RDF language.
097       * @return This object.
098       */
099      public SELF language(String value) {
100         language = value;
101         rdfSerializer = null; // Rebuild on next get
102         return self();
103      }
104
105      @Override
106      public SELF produces(String value) {
107         super.produces(value);
108         return self();
109      }
110
111      @Override
112      public SELF accept(String value) {
113         super.accept(value);
114         return self();
115      }
116
117      @Override
118      public SELF addBeanTypes(boolean value) {
119         super.addBeanTypes(value);
120         addBeanTypes = value;
121         rdfSerializer = null; // Rebuild on next get
122         return self();
123      }
124
125      @Override
126      public RdfStreamSerializer build() {
127         return cache(CACHE).build(RdfStreamSerializer.class);
128      }
129
130      @Override /* Overridden from Context.Builder<?> */
131      public abstract SELF copy();
132
133      @Override
134      public HashKey hashKey() {
135         return HashKey.of(super.hashKey(), language);
136      }
137
138      RdfSerializer getRdfSerializer() {
139         if (rdfSerializer == null)
140            rdfSerializer = RdfSerializer.create().language(language).addBeanTypesRdf(addBeanTypes).build();
141         return rdfSerializer;
142      }
143   }
144
145   /**
146    * Concrete default builder leaf for the non-subclassed {@link RdfStreamSerializer#create()} / {@link RdfStreamSerializer#copy()} path.
147    */
148   public static final class DefaultBuilder extends Builder<DefaultBuilder> {
149
150      DefaultBuilder() {}
151
152      DefaultBuilder(RdfStreamSerializer copyFrom) {
153         super(copyFrom);
154      }
155
156      DefaultBuilder(Builder<?> copyFrom) {
157         super(copyFrom);
158      }
159
160      @Override /* Overridden from Context.Builder<?> */
161      public DefaultBuilder copy() {
162         return new DefaultBuilder(this);
163      }
164   }
165
166   /**
167    * Creates a new builder.
168    *
169    * @return A new builder.
170    */
171   @SuppressWarnings({
172      "java:S1452", // Builder<?> wildcard return intentional; callers chain via fluent API without needing the concrete type
173      "java:S9149" // Public Juneau DSL factory; hiding parent create() is intentional.
174   })
175   public static Builder<?> create() {
176      return new DefaultBuilder();
177   }
178
179   private final String language;
180   private final RdfSerializer rdfSerializer;
181
182   /**
183    * Constructor.
184    *
185    * @param builder The builder.
186    *    <br>Cannot be <jk>null</jk>.
187    */
188   public RdfStreamSerializer(Builder<?> builder) {
189      super(reqnn("builder", builder).produces(getProduces(builder)).accept(getAccept(builder)));
190      language = builder.language;
191      rdfSerializer = builder.getRdfSerializer();
192   }
193
194   private static String getAccept(Builder<?> builder) {
195      if (builder.getAccept() != null)
196         return builder.getAccept();
197      return getProduces(builder);
198   }
199
200   private static String getProduces(Builder<?> builder) {
201      if (builder.getProduces() != null)
202         return builder.getProduces();
203      return switch (builder.language) {
204         case Constants.LANG_RDFTHRIFT -> "application/vnd.apache.thrift.binary";
205         case Constants.LANG_RDFPROTO -> "application/vnd.apache.protobuf";
206         default -> "application/octet-stream";
207      };
208   }
209
210   @Override
211   public Builder<?> copy() {
212      return new DefaultBuilder(this);
213   }
214
215   @Override
216   public RdfStreamSerializerSession.Builder createSession() {
217      return RdfStreamSerializerSession.create(this);
218   }
219
220   @Override
221   public RdfBeanMeta getRdfBeanMeta(BeanMeta<?> bm) {
222      return rdfSerializer.getRdfBeanMeta(bm);
223   }
224
225   @Override
226   public RdfBeanPropertyMeta getRdfBeanPropertyMeta(BeanPropertyMeta bpm) {
227      return rdfSerializer.getRdfBeanPropertyMeta(bpm);
228   }
229
230   @Override
231   public RdfClassMeta getRdfClassMeta(ClassMeta<?> cm) {
232      return rdfSerializer.getRdfClassMeta(cm);
233   }
234
235   @Override
236   public XmlBeanMeta getXmlBeanMeta(BeanMeta<?> bm) {
237      return rdfSerializer.getXmlBeanMeta(bm);
238   }
239
240   @Override
241   public XmlBeanPropertyMeta getXmlBeanPropertyMeta(BeanPropertyMeta bpm) {
242      return rdfSerializer.getXmlBeanPropertyMeta(bpm);
243   }
244
245   @Override
246   public XmlClassMeta getXmlClassMeta(ClassMeta<?> cm) {
247      return rdfSerializer.getXmlClassMeta(cm);
248   }
249
250   /**
251    * Returns the RDF language.
252    *
253    * @return The RDF language identifier.
254    */
255   public String getLanguage() {
256      return language;
257   }
258
259   /**
260    * Returns the delegated RdfSerializer.
261    *
262    * @return The delegated RdfSerializer.
263    */
264   protected RdfSerializer getRdfSerializer() {
265      return rdfSerializer;
266   }
267
268   /**
269    * Collection format - delegates to RdfSerializer.
270    *
271    * @return The RDF collection format.
272    */
273   public RdfCollectionFormat getCollectionFormat() {
274      return rdfSerializer.getCollectionFormat();
275   }
276
277   /**
278    * Juneau namespace - delegates to RdfSerializer.
279    *
280    * @return The Juneau namespace.
281    */
282   public Namespace getJuneauNs() {
283      return rdfSerializer.getJuneauNs();
284   }
285
286   /**
287    * Juneau BP namespace - delegates to RdfSerializer.
288    *
289    * @return The Juneau BP namespace.
290    */
291   public Namespace getJuneauBpNs() {
292      return rdfSerializer.getJuneauBpNs();
293   }
294
295   /**
296    * Namespaces - delegates to RdfSerializer.
297    *
298    * @return The RDF namespaces.
299    */
300   public Namespace[] getNamespaces() {
301      return rdfSerializer.getNamespaces();
302   }
303
304   /**
305    * Add literal types - delegates to RdfSerializer.
306    *
307    * @return True if literal types are added.
308    */
309   public boolean isAddLiteralTypes() {
310      return rdfSerializer.isAddLiteralTypes();
311   }
312
313   /**
314    * Add root property - delegates to RdfSerializer.
315    *
316    * @return True if root property is added.
317    */
318   public boolean isAddRootProp() {
319      return rdfSerializer.isAddRootProp();
320   }
321
322   /**
323    * Loose collections - delegates to RdfSerializer.
324    *
325    * @return True if loose collections are enabled.
326    */
327   public boolean isLooseCollections() {
328      return rdfSerializer.isLooseCollections();
329   }
330
331   /**
332    * Use XML namespaces - delegates to RdfSerializer.
333    *
334    * @return True if XML namespaces are used.
335    */
336   public boolean isUseXmlNamespaces() {
337      return rdfSerializer.isUseXmlNamespaces();
338   }
339
340   /**
341    * Auto-detect namespaces - delegates to RdfSerializer.
342    *
343    * @return True if auto-detect namespaces is enabled.
344    */
345   public boolean isAutoDetectNamespaces() {
346      return rdfSerializer.isAutoDetectNamespaces();
347   }
348
349   @Override
350   protected boolean isAddBeanTypes() {
351      return rdfSerializer.isAddBeanTypes();
352   }
353}