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.
016 */
017package org.apache.juneau.marshall.jena;
018
019
020import org.apache.juneau.commons.bean.*;
021import org.apache.juneau.commons.collections.*;
022import org.apache.juneau.marshall.*;
023import org.apache.juneau.marshall.parser.*;
024import org.apache.juneau.marshall.xml.*;
025import static org.apache.juneau.commons.utils.Shorts.*;
026
027/**
028 * Stream-based RDF parser for binary formats (RDF/THRIFT, RDF/PROTO).
029 *
030 * <p>
031 * Extends {@link InputStreamParser} and delegates RDF metadata to an internal {@link RdfParser}.
032 *
033 * <h5 class='section'>Notes:</h5><ul>
034 *    <li class='note'>This class is thread safe and reusable.
035 * </ul>
036 *
037 * <h5 class='section'>See Also:</h5><ul>
038 *    <li class='link'>{doc jmr.RdfDetails}
039 * </ul>
040 */
041@SuppressWarnings({
042   "java:S110", // Deep inheritance inherent to the RDF parser hierarchy.
043   "java:S1192" // Duplicated literals (argument/property names) read more clearly inline than as constants
044})
045public class RdfStreamParser extends InputStreamParser implements RdfMetaProvider {
046
047   /**
048    * Builder class.
049    */
050   @SuppressWarnings({
051      "java:S119" // 'SELF' (CRTP self-type) is intentional and clearer than a single-letter name.
052   })
053   public abstract static class Builder<SELF extends Builder<SELF>> extends InputStreamParser.Builder<SELF> {
054
055      private static final Cache<HashKey,RdfStreamParser> CACHE = Cache.of(HashKey.class, RdfStreamParser.class).build();
056
057      private String language;
058      private RdfParser rdfParser;
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         rdfParser = copyFrom.rdfParser;
077      }
078
079      /**
080       * Copy constructor.
081       *
082       * @param copyFrom The bean to copy from.
083       *    <br>Cannot be <jk>null</jk>.
084       */
085      protected Builder(RdfStreamParser copyFrom) {
086         super(reqnn("copyFrom", copyFrom));
087         language = copyFrom.language;
088         rdfParser = copyFrom.rdfParser;
089      }
090
091      /**
092       * RDF language (e.g. {@link Constants#LANG_RDFTHRIFT}, {@link Constants#LANG_RDFPROTO}).
093       *
094       * @param value The RDF language.
095       * @return This object.
096       */
097      public SELF language(String value) {
098         language = value;
099         rdfParser = null; // Rebuild on next get
100         return self();
101      }
102
103      @Override
104      public RdfStreamParser build() {
105         return cache(CACHE).build(RdfStreamParser.class);
106      }
107
108      @Override
109      public SELF consumes(String value) {
110         super.consumes(value);
111         return self();
112      }
113
114      @Override /* Overridden from Context.Builder<?> */
115      public abstract SELF copy();
116
117      @Override
118      public HashKey hashKey() {
119         return HashKey.of(super.hashKey(), language);
120      }
121
122      RdfParser getRdfParser() {
123         if (rdfParser == null)
124            rdfParser = RdfParser.create().language(language).build();
125         return rdfParser;
126      }
127   }
128
129   /**
130    * Concrete default builder leaf for the non-subclassed {@link RdfStreamParser#create()} / {@link RdfStreamParser#copy()} path.
131    */
132   public static final class DefaultBuilder extends Builder<DefaultBuilder> {
133
134      DefaultBuilder() {}
135
136      DefaultBuilder(RdfStreamParser copyFrom) {
137         super(copyFrom);
138      }
139
140      DefaultBuilder(Builder<?> copyFrom) {
141         super(copyFrom);
142      }
143
144      @Override /* Overridden from Context.Builder<?> */
145      public DefaultBuilder copy() {
146         return new DefaultBuilder(this);
147      }
148   }
149
150   /**
151    * Creates a new builder.
152    *
153    * @return A new builder.
154    */
155   @SuppressWarnings({
156      "java:S1452", // Builder<?> wildcard return intentional; callers chain via fluent API without needing the concrete type
157      "java:S9149" // Public Juneau DSL factory; hiding parent create() is intentional.
158   })
159   public static Builder<?> create() {
160      return new DefaultBuilder();
161   }
162
163   private final String language;
164   private final RdfParser rdfParser;
165
166   /**
167    * Constructor.
168    *
169    * @param builder The builder for this object.
170    *    <br>Cannot be <jk>null</jk>.
171    */
172   public RdfStreamParser(Builder<?> builder) {
173      super(reqnn("builder", builder).consumes(getConsumes(builder)));
174      language = builder.language;
175      rdfParser = builder.getRdfParser();
176   }
177
178   private static String getConsumes(Builder<?> builder) {
179      if (builder.getConsumes() != null)
180         return builder.getConsumes();
181      return switch (builder.language) {
182         case Constants.LANG_RDFTHRIFT -> "application/vnd.apache.thrift.binary";
183         case Constants.LANG_RDFPROTO -> "application/vnd.apache.protobuf";
184         default -> "application/octet-stream";
185      };
186   }
187
188   @Override
189   public Builder<?> copy() {
190      return new DefaultBuilder(this);
191   }
192
193   @Override
194   public RdfStreamParserSession.Builder createSession() {
195      return RdfStreamParserSession.create(this);
196   }
197
198   @Override
199   public RdfBeanMeta getRdfBeanMeta(BeanMeta<?> bm) {
200      return rdfParser.getRdfBeanMeta(bm);
201   }
202
203   @Override
204   public RdfBeanPropertyMeta getRdfBeanPropertyMeta(BeanPropertyMeta bpm) {
205      return rdfParser.getRdfBeanPropertyMeta(bpm);
206   }
207
208   @Override
209   public RdfClassMeta getRdfClassMeta(ClassMeta<?> cm) {
210      return rdfParser.getRdfClassMeta(cm);
211   }
212
213   @Override
214   public XmlBeanMeta getXmlBeanMeta(BeanMeta<?> bm) {
215      return rdfParser.getXmlBeanMeta(bm);
216   }
217
218   @Override
219   public XmlBeanPropertyMeta getXmlBeanPropertyMeta(BeanPropertyMeta bpm) {
220      return rdfParser.getXmlBeanPropertyMeta(bpm);
221   }
222
223   @Override
224   public XmlClassMeta getXmlClassMeta(ClassMeta<?> cm) {
225      return rdfParser.getXmlClassMeta(cm);
226   }
227
228   /**
229    * Returns the RDF language.
230    *
231    * @return The RDF language.
232    */
233   public String getLanguage() {
234      return language;
235   }
236
237   /**
238    * Returns the delegated RdfParser.
239    *
240    * @return The delegated RdfParser.
241    */
242   protected RdfParser getRdfParser() {
243      return rdfParser;
244   }
245
246   /** Collection format - delegates to RdfParser. */
247   protected RdfCollectionFormat getCollectionFormat() {
248      return rdfParser.getCollectionFormat();
249   }
250
251   /** Juneau namespace - delegates to RdfParser. */
252   protected Namespace getJuneauNs() {
253      return rdfParser.getJuneauNs();
254   }
255
256   /** Juneau BP namespace - delegates to RdfParser. */
257   protected Namespace getJuneauBpNs() {
258      return rdfParser.getJuneauBpNs();
259   }
260
261   /** Loose collections - delegates to RdfParser. */
262   protected boolean isLooseCollections() {
263      return rdfParser.isLooseCollections();
264   }
265
266   /** Trim whitespace - delegates to RdfParser. */
267   protected boolean isTrimWhitespace() {
268      return rdfParser.isTrimWhitespace();
269   }
270}