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.*;
018import java.util.concurrent.*;
019
020import org.apache.juneau.*;
021import org.apache.juneau.annotation.*;
022import org.apache.juneau.parser.*;
023import org.apache.juneau.xml.*;
024
025/**
026 * Parses RDF into POJOs.
027 *
028 * <h5 class='topic'>Behavior-specific subclasses</h5>
029 *
030 * The following direct subclasses are provided for language-specific parsers:
031 * <ul class='spaced-list'>
032 *    <li>
033 *       {@link RdfXmlParser} - RDF/XML and RDF/XML-ABBREV.
034 *    <li>
035 *       {@link NTripleParser} - N-TRIPLE.
036 *    <li>
037 *       {@link TurtleParser} - TURTLE.
038 *    <li>
039 *       {@link N3Parser} - N3.
040 * </ul>
041 *
042 * <ul class='seealso'>
043 *    <li class='link'>{@doc juneau-marshall-rdf}
044 * </ul>
045 */
046@ConfigurableContext(prefixes={RdfCommon.PREFIX,RdfParser.PREFIX})
047public class RdfParser extends ReaderParser implements RdfCommon, RdfMetaProvider {
048
049   private static final Namespace
050      DEFAULT_JUNEAU_NS = Namespace.create("j", "http://www.apache.org/juneau/"),
051      DEFAULT_JUNEAUBP_NS = Namespace.create("jp", "http://www.apache.org/juneaubp/");
052
053   //-------------------------------------------------------------------------------------------------------------------
054   // Configurable properties
055   //-------------------------------------------------------------------------------------------------------------------
056
057   static final String PREFIX = "RdfParser";
058
059   /**
060    * Configuration property:  Trim whitespace from text elements.
061    *
062    * <h5 class='section'>Property:</h5>
063    * <ul class='spaced-list'>
064    *    <li><b>ID:</b>  {@link org.apache.juneau.jena.RdfParser#RDF_trimWhitespace RDF_trimWhitespace}
065    *    <li><b>Name:</b>  <js>"RdfParser.trimWhitespace.b"</js>
066    *    <li><b>Data type:</b>  <jk>boolean</jk>
067    *    <li><b>System property:</b>  <c>RdfParser.trimWhitespace</c>
068    *    <li><b>Environment variable:</b>  <c>RDFPARSER_TRIMWHITESPACE</c>
069    *    <li><b>Default:</b>  <jk>false</jk>
070    *    <li><b>Session property:</b>  <jk>false</jk>
071    *    <li><b>Annotations:</b>
072    *       <ul>
073    *          <li class='ja'>{@link org.apache.juneau.jena.annotation.RdfConfig#trimWhitespace()}
074    *       </ul>
075    *    <li><b>Methods:</b>
076    *       <ul>
077    *          <li class='jm'>{@link org.apache.juneau.jena.RdfParserBuilder#trimWhitespace(boolean)}
078    *          <li class='jm'>{@link org.apache.juneau.jena.RdfParserBuilder#trimWhitespace()}
079    *       </ul>
080    * </ul>
081    *
082    * <h5 class='section'>Description:</h5>
083    * <p>
084    * If <jk>true</jk>, whitespace in text elements will be automatically trimmed.
085    *
086    * <h5 class='section'>Example:</h5>
087    * <p class='bcode w800'>
088    *    <jc>// Create an RDF parser that trims whitespace.</jc>
089    *    ReaderParser p = RdfParser
090    *       .<jsm>create</jsm>()
091    *       .xml()
092    *       .trimWhitespace()
093    *       .build();
094    *
095    *    <jc>// Same, but use property.</jc>
096    *    ReaderParser p = RdfParser
097    *       .<jsm>create</jsm>()
098    *       .xml()
099    *       .set(<jsf>RDF_trimWhitespace</jsf>, <jk>true</jk>)
100    *       .build();
101    * </p>
102    */
103   public static final String RDF_trimWhitespace = PREFIX + ".trimWhitespace.b";
104
105   //-------------------------------------------------------------------------------------------------------------------
106   // Instance
107   //-------------------------------------------------------------------------------------------------------------------
108
109   private final boolean trimWhitespace, looseCollections;
110   private final String rdfLanguage;
111   private final Namespace juneauNs, juneauBpNs;
112   private final RdfCollectionFormat collectionFormat;
113
114   final Map<String,Object> jenaProperties;
115
116   private final Map<ClassMeta<?>,RdfClassMeta> rdfClassMetas = new ConcurrentHashMap<>();
117   private final Map<BeanMeta<?>,RdfBeanMeta> rdfBeanMetas = new ConcurrentHashMap<>();
118   private final Map<BeanPropertyMeta,RdfBeanPropertyMeta> rdfBeanPropertyMetas = new ConcurrentHashMap<>();
119
120   private final Map<ClassMeta<?>,XmlClassMeta> xmlClassMetas = new ConcurrentHashMap<>();
121   private final Map<BeanMeta<?>,XmlBeanMeta> xmlBeanMetas = new ConcurrentHashMap<>();
122   private final Map<BeanPropertyMeta,XmlBeanPropertyMeta> xmlBeanPropertyMetas = new ConcurrentHashMap<>();
123
124   /**
125    * Constructor.
126    *
127    * @param ps The property store containing all the settings for this object.
128    * @param consumes The list of media types that this parser consumes (e.g. <js>"application/json"</js>).
129    */
130   public RdfParser(PropertyStore ps, String...consumes) {
131      super(ps, consumes);
132      trimWhitespace = getBooleanProperty(RDF_trimWhitespace, false);
133      looseCollections = getBooleanProperty(RDF_looseCollections, false);
134      rdfLanguage = getStringProperty(RDF_language, "RDF/XML-ABBREV");
135      juneauNs = getInstanceProperty(RDF_juneauNs, Namespace.class, DEFAULT_JUNEAU_NS);
136      juneauBpNs = getInstanceProperty(RDF_juneauBpNs, Namespace.class, DEFAULT_JUNEAUBP_NS);
137      collectionFormat = getProperty(RDF_collectionFormat, RdfCollectionFormat.class, RdfCollectionFormat.DEFAULT);
138
139      Map<String,Object> m = new TreeMap<>();
140      for (String k : getPropertyKeys("RdfCommon"))
141         if (k.startsWith("jena."))
142            m.put(k.substring(5), getProperty("RdfCommon." + k));
143      jenaProperties = unmodifiableMap(m);
144   }
145
146   /**
147    * Constructor.
148    *
149    * @param ps The property store containing all the settings for this object.
150    */
151   public RdfParser(PropertyStore ps) {
152      this(ps, "text/xml+rdf");
153   }
154
155   @Override /* Context */
156   public RdfParserBuilder builder() {
157      return new RdfParserBuilder(getPropertyStore());
158   }
159
160   /**
161    * Instantiates a new clean-slate {@link RdfParserBuilder} object.
162    *
163    * <p>
164    * This is equivalent to simply calling <code><jk>new</jk> RdfParserBuilder()</code>.
165    *
166    * <p>
167    * Note that this method creates a builder initialized to all default settings, whereas {@link #builder()} copies
168    * the settings of the object called on.
169    *
170    * @return A new {@link RdfParserBuilder} object.
171    */
172   public static RdfParserBuilder create() {
173      return new RdfParserBuilder();
174   }
175
176   @Override /* Parser */
177   public RdfParserSession createSession() {
178      return createSession(createDefaultSessionArgs());
179   }
180
181   @Override /* Parser */
182   public RdfParserSession createSession(ParserSessionArgs args) {
183      return new RdfParserSession(this, args);
184   }
185
186   //-----------------------------------------------------------------------------------------------------------------
187   // Extended metadata
188   //-----------------------------------------------------------------------------------------------------------------
189
190   @Override /* RdfMetaProvider */
191   public RdfClassMeta getRdfClassMeta(ClassMeta<?> cm) {
192      RdfClassMeta m = rdfClassMetas.get(cm);
193      if (m == null) {
194         m = new RdfClassMeta(cm, this);
195         rdfClassMetas.put(cm, m);
196      }
197      return m;
198   }
199
200   @Override /* RdfMetaProvider */
201   public RdfBeanMeta getRdfBeanMeta(BeanMeta<?> bm) {
202      RdfBeanMeta m = rdfBeanMetas.get(bm);
203      if (m == null) {
204         m = new RdfBeanMeta(bm, this);
205         rdfBeanMetas.put(bm, m);
206      }
207      return m;
208   }
209
210   @Override /* RdfMetaProvider */
211   public RdfBeanPropertyMeta getRdfBeanPropertyMeta(BeanPropertyMeta bpm) {
212      RdfBeanPropertyMeta m = rdfBeanPropertyMetas.get(bpm);
213      if (m == null) {
214         m = new RdfBeanPropertyMeta(bpm.getDelegateFor(), this);
215         rdfBeanPropertyMetas.put(bpm, m);
216      }
217      return m;
218   }
219
220   @Override /* XmlMetaProvider */
221   public XmlClassMeta getXmlClassMeta(ClassMeta<?> cm) {
222      XmlClassMeta m = xmlClassMetas.get(cm);
223      if (m == null) {
224         m = new XmlClassMeta(cm, this);
225         xmlClassMetas.put(cm, m);
226      }
227      return m;
228   }
229
230   @Override /* XmlMetaProvider */
231   public XmlBeanMeta getXmlBeanMeta(BeanMeta<?> bm) {
232      XmlBeanMeta m = xmlBeanMetas.get(bm);
233      if (m == null) {
234         m = new XmlBeanMeta(bm, this);
235         xmlBeanMetas.put(bm, m);
236      }
237      return m;
238   }
239
240   @Override /* XmlMetaProvider */
241   public XmlBeanPropertyMeta getXmlBeanPropertyMeta(BeanPropertyMeta bpm) {
242      XmlBeanPropertyMeta m = xmlBeanPropertyMetas.get(bpm);
243      if (m == null) {
244         m = new XmlBeanPropertyMeta(bpm.getDelegateFor(), this);
245         xmlBeanPropertyMetas.put(bpm, m);
246      }
247      return m;
248   }
249
250   //-----------------------------------------------------------------------------------------------------------------
251   // Common properties
252   //-----------------------------------------------------------------------------------------------------------------
253
254   /**
255    * Configuration property:  RDF format for representing collections and arrays.
256    *
257    * @see #RDF_collectionFormat
258    * @return
259    *    RDF format for representing collections and arrays.
260    */
261   protected final RdfCollectionFormat getCollectionFormat() {
262      return collectionFormat;
263   }
264
265   /**
266    * Configuration property:  Default XML namespace for bean properties.
267    *
268    * @see #RDF_juneauBpNs
269    * @return
270    *    Default XML namespace for bean properties.
271    */
272   protected final Namespace getJuneauBpNs() {
273      return juneauBpNs;
274   }
275
276   /**
277    * Configuration property:  XML namespace for Juneau properties.
278    *
279    * @see #RDF_juneauNs
280    * @return
281    *    XML namespace for Juneau properties.
282    */
283   protected final Namespace getJuneauNs() {
284      return juneauNs;
285   }
286
287   /**
288    * Configuration property:  RDF language.
289    *
290    * @see #RDF_language
291    * @return
292    *    The RDF language to use.
293    */
294   protected final String getLanguage() {
295      return rdfLanguage;
296   }
297
298   /**
299    * Configuration property:  Collections should be serialized and parsed as loose collections.
300    *
301    * @see #RDF_looseCollections
302    * @return
303    *    <jk>true</jk> if collections of resources are handled as loose collections of resources in RDF instead of
304    *    resources that are children of an RDF collection (e.g. Sequence, Bag).
305    */
306   protected final boolean isLooseCollections() {
307      return looseCollections;
308   }
309
310   //-----------------------------------------------------------------------------------------------------------------
311   // Jena properties
312   //-----------------------------------------------------------------------------------------------------------------
313
314   /**
315    * Configuration property:  All Jena-related configuration properties.
316    *
317    * @return
318    *    A map of all Jena-related configuration properties.
319    */
320   protected final Map<String,Object> getJenaProperties() {
321      return jenaProperties;
322   }
323
324   //-----------------------------------------------------------------------------------------------------------------
325   // Properties
326   //-----------------------------------------------------------------------------------------------------------------
327
328   /**
329    * Configuration property:  Trim whitespace from text elements.
330    *
331    * @see #RDF_trimWhitespace
332    * @return
333    *    <jk>true</jk> if whitespace in text elements will be automatically trimmed.
334    */
335   protected final boolean isTrimWhitespace() {
336      return trimWhitespace;
337   }
338
339   //-----------------------------------------------------------------------------------------------------------------
340   // Other methods
341   //-----------------------------------------------------------------------------------------------------------------
342
343   @Override /* Context */
344   public ObjectMap toMap() {
345      return super.toMap()
346         .append("RdfParser", new DefaultFilteringObjectMap()
347            .append("trimWhitespace", trimWhitespace)
348            .append("looseCollections", looseCollections)
349            .append("rdfLanguage", rdfLanguage)
350            .append("juneauNs", juneauNs)
351            .append("juneauBpNs", juneauBpNs)
352            .append("collectionFormat", collectionFormat)
353         );
354   }
355}