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
019import static org.apache.juneau.commons.utils.Shorts.*;
020import static org.apache.juneau.commons.utils.SystemUtils.*;
021
022import java.util.*;
023import java.util.concurrent.*;
024
025import org.apache.juneau.commons.bean.*;
026import org.apache.juneau.commons.collections.*;
027import org.apache.juneau.marshall.*;
028import org.apache.juneau.marshall.parser.*;
029import org.apache.juneau.marshall.xml.*;
030
031/**
032 * Parses RDF into POJOs.
033 *
034 * <h5 class='topic'>Behavior-specific subclasses</h5>
035 * <p>
036 * The following direct subclasses are provided for language-specific parsers:
037 * <ul class='spaced-list'>
038 *    <li>
039 *       {@link RdfXmlParser} - RDF/XML and RDF/XML-ABBREV.
040 *    <li>
041 *       {@link NTripleParser} - N-TRIPLE.
042 *    <li>
043 *       {@link TurtleParser} - TURTLE.
044 *    <li>
045 *       {@link N3Parser} - N3.
046 * </ul>
047 *
048 * <h5 class='section'>Notes:</h5><ul>
049 *    <li class='note'>This class is thread safe and reusable.
050 * </ul>
051 *
052 * <h5 class='section'>See Also:</h5><ul>
053 *    <li class='link'>{doc jmr.RdfDetails}
054
055 * </ul>
056 */
057@SuppressWarnings({
058   "java:S100", // RDF method names (getRdfXml, isXmlLang, etc.) use domain-specific casing
059   "java:S110", // Deep inheritance inherent to the RDF parser hierarchy.
060   "java:S1192" // Duplicated literals (argument/property names) read more clearly inline than as constants
061})
062public class RdfParser extends ReaderParser implements RdfMetaProvider {
063
064   /**
065    * Builder class.
066    */
067   @SuppressWarnings({
068      "java:S119" // 'SELF' (CRTP self-type) is intentional and clearer than a single-letter name.
069   })
070   public abstract static class Builder<SELF extends Builder<SELF>> extends ReaderParser.Builder<SELF> {
071
072      private static final Cache<HashKey,RdfParser> CACHE = Cache.of(HashKey.class, RdfParser.class).build();
073
074      private static final Namespace DEFAULT_JUNEAU_NS = Namespace.of("j", "http://www.apache.org/juneau/");
075      private static final Namespace DEFAULT_JUNEAUBP_NS = Namespace.of("jp", "http://www.apache.org/juneaubp/");
076
077      boolean trimWhitespace;
078      boolean looseCollections;
079      String language;
080      Namespace juneauNs;
081      Namespace juneauBpNs;
082      RdfCollectionFormat collectionFormat;
083      Map<String,Object> jenaSettings = new TreeMap<>();
084
085      /**
086       * Constructor, default settings.
087       */
088      protected Builder() {
089         super();
090         trimWhitespace = env("Rdf.trimWhitespace", false);
091         looseCollections = env("Rdf.looseCollections", false);
092         language = env("Rdf.language", "RDF/XML-ABBREV");
093         collectionFormat = env("Rdf.collectionFormat", RdfCollectionFormat.DEFAULT);
094         juneauNs = DEFAULT_JUNEAU_NS;
095         juneauBpNs = DEFAULT_JUNEAUBP_NS;
096         jenaSettings = new TreeMap<>();
097      }
098
099      /**
100       * Copy constructor.
101       *
102       * @param copyFrom The builder to copy from.
103       *    <br>Cannot be <jk>null</jk>.
104       */
105      protected Builder(Builder<?> copyFrom) {
106         super(reqnn("copyFrom", copyFrom));
107         collectionFormat = copyFrom.collectionFormat;
108         juneauBpNs = copyFrom.juneauBpNs;
109         juneauNs = copyFrom.juneauNs;
110         language = copyFrom.language;
111         looseCollections = copyFrom.looseCollections;
112         trimWhitespace = copyFrom.trimWhitespace;
113         jenaSettings = new TreeMap<>(copyFrom.jenaSettings);
114      }
115
116      /**
117       * Copy constructor.
118       *
119       * @param copyFrom The bean to copy from.
120       *    <br>Cannot be <jk>null</jk>.
121       */
122      protected Builder(RdfParser copyFrom) {
123         super(reqnn("copyFrom", copyFrom));
124         collectionFormat = copyFrom.collectionFormat;
125         juneauBpNs = copyFrom.juneauBpNs;
126         juneauNs = copyFrom.juneauNs;
127         language = copyFrom.language;
128         looseCollections = copyFrom.looseCollections;
129         trimWhitespace = copyFrom.trimWhitespace;
130         jenaSettings = new TreeMap<>(copyFrom.jenaSettings);
131      }
132
133      @Override /* Overridden from Context.Builder<?> */
134      public RdfParser build() {
135         return cache(CACHE).build(RdfParser.class);
136      }
137
138      /**
139       * RDF format for representing collections and arrays.
140       *
141       * <ul class='values spaced-list'>
142       *    <li>
143       *       <js>"DEFAULT"</js> - Default format.  The default is an RDF Sequence container.
144       *    <li>
145       *       <js>"SEQ"</js> - RDF Sequence container.
146       *    <li>
147       *       <js>"BAG"</js> - RDF Bag container.
148       *    <li>
149       *       <js>"LIST"</js> - RDF List container.
150       *    <li>
151       *       <js>"MULTI_VALUED"</js> - Multi-valued properties.
152       * </ul>
153       *
154       * @param value The new value for this property.
155       *    <br>Cannot be <jk>null</jk>.
156       * @return This object.
157       */
158      public SELF collectionFormat(RdfCollectionFormat value) {
159         collectionFormat = reqnn("value", value);
160         return self();
161      }
162
163      @Override /* Overridden from Context.Builder<?> */
164      public abstract SELF copy();
165
166      @Override /* Overridden from Context.Builder<?> */
167      public HashKey hashKey() {
168         // @formatter:off
169         return HashKey.of(
170            super.hashKey(),
171            trimWhitespace,
172            looseCollections,
173            language,
174            collectionFormat,
175            juneauNs,
176            juneauBpNs,
177            jenaSettings
178         );
179         // @formatter:on
180      }
181
182      /**
183       * Default XML namespace for bean properties.
184       *
185       * @param value
186       *    The new value for this property.
187       *    <br>The default is <code>{j:<js>'http://www.apache.org/juneaubp/'</js>}</code>.
188       *    <br>Cannot be <jk>null</jk>.
189       * @return This object.
190       */
191      public SELF juneauBpNs(Namespace value) {
192         juneauBpNs = reqnn("value", value);
193         return self();
194      }
195
196      /**
197       * XML namespace for Juneau properties.
198       *
199       * @param value
200       *    The new value for this property.
201       *    <br>The default is <code>{j:<js>'http://www.apache.org/juneau/'</js>}</code>.
202       *    <br>Cannot be <jk>null</jk>.
203       * @return This object.
204       */
205      public SELF juneauNs(Namespace value) {
206         juneauNs = reqnn("value", value);
207         return self();
208      }
209
210      /**
211       * RDF language.
212       *
213       * <p>
214       * Can be any of the following:
215       * <ul class='spaced-list'>
216       *    <li>
217       *       <js>"RDF/XML"</js>
218       *    <li>
219       *       <js>"RDF/XML-ABBREV"</js> (default)
220       *    <li>
221       *       <js>"N-TRIPLE"</js>
222       *    <li>
223       *       <js>"N3"</js> - General name for the N3 writer.
224       *       Will make a decision on exactly which writer to use (pretty writer, plain writer or simple writer) when
225       *       created.
226       *       Default is the pretty writer but can be overridden with system property
227       *       <c>org.apache.jena.n3.N3JenaWriter.writer</c>.
228       *    <li>
229       *       <js>"N3-PP"</js> - Name of the N3 pretty writer.
230       *       The pretty writer uses a frame-like layout, with prefixing, clustering like properties and embedding
231       *       one-referenced bNodes.
232       *    <li>
233       *       <js>"N3-PLAIN"</js> - Name of the N3 plain writer.
234       *       The plain writer writes records by subject.
235       *    <li>
236       *       <js>"N3-TRIPLES"</js> - Name of the N3 triples writer.
237       *       This writer writes one line per statement, like N-Triples, but does N3-style prefixing.
238       *    <li>
239       *       <js>"TURTLE"</js> -  Turtle writer.
240       *       http://www.dajobe.org/2004/01/turtle/
241       * </ul>
242       *
243       * <h5 class='section'>See Also:</h5><ul>
244       *    <li class='jm'>{@link RdfParser.Builder#n3()}
245       *    <li class='jm'>{@link RdfParser.Builder#ntriple()}
246       *    <li class='jm'>{@link RdfParser.Builder#turtle()}
247       *    <li class='jm'>{@link RdfParser.Builder#xml()}
248       * </ul>
249       *
250       * @param value The new value for this property.
251       *    <br>Cannot be <jk>null</jk>.
252       * @return This object.
253       */
254      public SELF language(String value) {
255         language = reqnn("value", value);
256         return self();
257      }
258
259      /**
260       * Collections should be serialized and parsed as loose collections.
261       *
262       * <p>
263       * When specified, collections of resources are handled as loose collections of resources in RDF instead of
264       * resources that are children of an RDF collection (e.g. Sequence, Bag).
265       *
266       * <p>
267       * Note that this setting is specialized for RDF syntax, and is incompatible with the concept of
268       * losslessly representing POJO models, since the tree structure of these POJO models are lost
269       * when serialized as loose collections.
270       *
271       * <p>
272       * This setting is typically only useful if the beans being parsed into do not have a bean property
273       * annotated with {@link Rdf#beanUri @Rdf(beanUri=true)}.
274       *
275       * <h5 class='section'>Example:</h5>
276       * <p class='bjava'>
277       *    WriterSerializer <jv>serializer</jv> = RdfSerializer
278       *       .<jsm>create</jsm>()
279       *       .xmlabbrev()
280       *       .looseCollections()
281       *       .build();
282       *
283       *    ReaderParser <jv>parser</jv> = RdfParser
284       *       .<jsm>create</jsm>()
285       *       .xml()
286       *       .looseCollections()
287       *       .build();
288       *
289       *    List&lt;MyBean&gt; <jv>list</jv> = <jsm>createListOfMyBeans</jsm>();
290       *
291       *    <jc>// Serialize to RDF/XML as loose resources</jc>
292       *    String <jv>rdfXml</jv> = <jv>serializer</jv>.write(<jv>list</jv>);
293       *
294       *    <jc>// Parse back into a Java collection</jc>
295       *    <jv>list</jv> = <jv>parser</jv>.read(<jv>rdfXml</jv>, LinkedList.<jk>class</jk>, MyBean.<jk>class</jk>);
296       *
297       *    MyBean[] <jv>bean</jv> = <jsm>createArrayOfMyBeans</jsm>();
298       *
299       *    <jc>// Serialize to RDF/XML as loose resources</jc>
300       *    <jv>rdfXml</jv> = <jv>serializer</jv>.write(<jv>beans</jv>);
301       *
302       *    <jc>// Parse back into a bean array</jc>
303       *    <jv>beans</jv> = <jv>parser</jv>.read(<jv>rdfXml</jv>, MyBean[].<jk>class</jk>);
304       * </p>
305       *
306       * @return This object.
307       */
308      public SELF looseCollections() {
309         return looseCollections(true);
310      }
311
312      /**
313       * Same as {@link #looseCollections()} but explicitly specifies the setting value.
314       *
315       * @param value The new value for this property.
316       * @return This object.
317       */
318      public SELF looseCollections(boolean value) {
319         looseCollections = value;
320         return self();
321      }
322
323      /**
324       * RDF language.
325       *
326       * <p>
327       * Shortcut for calling <code>language(<jsf>LANG_N3</jsf>)</code>.
328       *
329       * @return This object.
330       */
331      public SELF n3() {
332         return language(Constants.LANG_N3);
333      }
334
335      /**
336       * N3/Turtle property: <c>disableAbbrevBaseURI</c>.
337       *
338       * <p>
339       * Controls whether to use abbreviations <c>&lt;&gt;</c> or <c>&lt;#&gt;</c>.
340       *
341       * @return This object.
342       */
343      public SELF n3_disableAbbrevBaseUri() {
344         return n3_disableAbbrevBaseUri(true);
345      }
346
347      /**
348       * N3/Turtle property: <c>disableAbbrevBaseURI</c>.
349       *
350       * <p>
351       * Controls whether to use abbreviations <c>&lt;&gt;</c> or <c>&lt;#&gt;</c>.
352       *
353       * @param value
354       *    The new value for this setting.
355       * @return This object.
356       */
357      public SELF n3_disableAbbrevBaseUri(boolean value) {
358         return jena("n3.disableAbbrevBaseUri", value);
359      }
360
361      /**
362       * N3/Turtle property: <c>disableObjectLists</c>.
363       *
364       * <p>
365       * Don't print object lists as comma separated lists.
366       *
367       * @return This object.
368       */
369      public SELF n3_disableObjectLists() {
370         return n3_disableObjectLists(true);
371      }
372
373      /**
374       * N3/Turtle property: <c>disableObjectLists</c>.
375       *
376       * <p>
377       * Don't print object lists as comma separated lists.
378       *
379       * @param value
380       *    The new value for this property.
381       * @return This object.
382       */
383      public SELF n3_disableObjectLists(boolean value) {
384         return jena("n3.disableObjectLists", value);
385      }
386
387      /**
388       * N3/Turtle property: <c>disableUseDoubles</c>.
389       *
390       * <p>
391       * Disallow the use of doubles as <c>123.456</c>.
392       *
393       * @return This object.
394       */
395      public SELF n3_disableUseDoubles() {
396         return n3_disableUseDoubles(true);
397      }
398
399      /**
400       * N3/Turtle property: <c>disableUseDoubles</c>.
401       *
402       * <p>
403       * Disallow the use of doubles as <c>123.456</c>.
404       *
405       * @param value
406       *    The new value for this setting.
407       * @return This object.
408       */
409      public SELF n3_disableUseDoubles(boolean value) {
410         return jena("n3.disableUseDoubles", value);
411      }
412
413      /**
414       * N3/Turtle property: <c>disableUsePropertySymbols</c>.
415       *
416       * <p>
417       * Controls whether to use <c>a</c>, <c>=</c> and <c>=&gt;</c> in output
418       *
419       * @return This object.
420       */
421      public SELF n3_disableUsePropertySymbols() {
422         return n3_disableUsePropertySymbols(true);
423      }
424
425      /**
426       * N3/Turtle property: <c>disableUsePropertySymbols</c>.
427       *
428       * <p>
429       * Controls whether to use <c>a</c>, <c>=</c> and <c>=&gt;</c> in output
430       *
431       * @param value
432       *    The new value for this setting.
433       * @return This object.
434       */
435      public SELF n3_disableUsePropertySymbols(boolean value) {
436         return jena("n3.disableUsePropertySymbols", value);
437      }
438
439      /**
440       * N3/Turtle property: <c>disableUseTripleQuotedStrings</c>.
441       *
442       * <p>
443       * Disallow the use of <c>"""</c> to delimit long strings.
444       *
445       * @return This object.
446       */
447      public SELF n3_disableUseTripleQuotedStrings() {
448         return n3_disableUseTripleQuotedStrings(true);
449      }
450
451      /**
452       * N3/Turtle property: <c>disableUseTripleQuotedStrings</c>.
453       *
454       * <p>
455       * Disallow the use of <c>"""</c> to delimit long strings.
456       *
457       * @param value
458       *    The new value for this setting.
459       * @return This object.
460       */
461      public SELF n3_disableUseTripleQuotedStrings(boolean value) {
462         return jena("n3.disableUseTripleQuotedStrings", value);
463      }
464
465      /**
466       * N3/Turtle property: <c>indentProperty</c>.
467       *
468       * <p>
469       * Width to indent properties.
470       *
471       * @param value
472       *    The new value for this property.
473       * @return This object.
474       */
475      public SELF n3_indentProperty(int value) {
476         return jena("n3.indentProperty", value);
477      }
478
479      /**
480       * N3/Turtle property: <c>minGap</c>.
481       *
482       * <p>
483       * Minimum gap between items on a line.
484       *
485       * @param value
486       *    The new value for this property.
487       * @return This object.
488       */
489      public SELF n3_minGap(int value) {
490         return jena("n3.minGap", value);
491      }
492
493      /**
494       * N3/Turtle property: <c>propertyColumn</c>.
495       *
496       * <p>
497       * Width of the property column.
498       *
499       * @param value
500       *    The new value for this property.
501       * @return This object.
502       */
503      public SELF n3_propertyColumn(int value) {
504         return jena("n3.propertyColumn", value);
505      }
506
507      /**
508       * N3/Turtle property: <c>subjectColumn</c>.
509       *
510       * <p>
511       * If the subject is shorter than this value, the first property may go on the same line.
512       *
513       * @param value
514       *    The new value for this property.
515       * @return This object.
516       */
517      public SELF n3_subjectColumn(int value) {
518         return jena("n3.subjectColumn", value);
519      }
520
521      /**
522       * N3/Turtle property: <c>widePropertyLen</c>.
523       *
524       * <p>
525       * Width of the property column.
526       * Must be longer than <c>propertyColumn</c>.
527       *
528       * @param value
529       *    The new value for this property.
530       * @return This object.
531       */
532      public SELF n3_widePropertyLen(int value) {
533         return jena("n3.widePropertyLen", value);
534      }
535
536      /**
537       * RDF language.
538       *
539       * <p>
540       * Shortcut for calling <code>language(<jsf>LANG_NTRIPLE</jsf>)</code>.
541       *
542       * @return This object.
543       */
544      public SELF ntriple() {
545         return language(Constants.LANG_NTRIPLE);
546      }
547
548      /**
549       * RDF/XML property: <c>allowBadURIs</c>.
550       *
551       * <p>
552       * URIs in the graph are, by default, checked prior to serialization.
553       *
554       * @return This object.
555       */
556      public SELF rdfxml_allowBadUris() {
557         return rdfxml_allowBadUris(true);
558      }
559
560      /**
561       * RDF/XML property: <c>allowBadURIs</c>.
562       *
563       * <p>
564       * URIs in the graph are, by default, checked prior to serialization.
565       *
566       * @param value
567       *    The new value for this property.
568       * @return This object.
569       */
570      public SELF rdfxml_allowBadUris(boolean value) {
571         return jena("rdfXml.allowBadURIs", value);
572      }
573
574      /**
575       * RDF/XML property: <c>attributeQuoteChar</c>.
576       *
577       * <p>
578       * The XML attribute quote character.
579       *
580       * @param value
581       *    The new value for this property.
582       * @return This object.
583       */
584      public SELF rdfxml_attributeQuoteChar(char value) {
585         return jena("rdfXml.attributeQuoteChar", value);
586      }
587
588      /**
589       * RDF/XML property: <c>blockRules</c>.
590       *
591       * <p>
592       * A list of <c>Resource</c> or a <c>String</c> being a comma separated list of fragment IDs from
593       * {doc http://www.w3.org/TR/rdf-syntax-grammar RDF Syntax Grammar} indicating grammar
594       * rules that will not be used.
595       *
596       * @param value
597       *    The new value for this property.
598       * @return This object.
599       */
600      public SELF rdfxml_blockRules(String value) {
601         return jena("rdfXml.blockRules", value);
602      }
603
604      /**
605       * RDF/XML property: <c>disableShowDoctypeDeclaration</c>.
606       *
607       * <p>
608       * If disabled, an XML doctype declaration isn't included in the output.
609       * This declaration includes a <c>!ENTITY</c> declaration for each prefix mapping in the model, and any
610       * attribute value that starts with the URI of that mapping is written as starting with the corresponding entity
611       * invocation.
612       *
613       * @return This object.
614       */
615      public SELF rdfxml_disableShowDoctypeDeclaration() {
616         return rdfxml_disableShowDoctypeDeclaration(true);
617      }
618
619      /**
620       * RDF/XML property: <c>disableShowDoctypeDeclaration</c>.
621       *
622       * <p>
623       * If disabled, an XML doctype declaration isn't included in the output.
624       * This declaration includes a <c>!ENTITY</c> declaration for each prefix mapping in the model, and any
625       * attribute value that starts with the URI of that mapping is written as starting with the corresponding entity
626       * invocation.
627       *
628       * @param value
629       *    The new value for this property.
630       * @return This object.
631       */
632      public SELF rdfxml_disableShowDoctypeDeclaration(boolean value) {
633         return jena("rdfXml.disableShowDoctypeDeclaration", value);
634      }
635
636      /**
637       * RDF/XML ARP property: <c>embedding</c>.
638       *
639       * <p>
640       * Sets ARP to look for RDF embedded within an enclosing XML document.
641       *
642       * <h5 class='section'>See Also:</h5><ul>
643       *    <li>
644       *       {doc ext.ARP/ARPOptions.html#setEmbedding(boolean) ARPOptions.setEmbedding(boolean)}
645       * </ul>
646       *
647       * @return This object.
648       */
649      public SELF rdfxml_embedding() {
650         return rdfxml_embedding(true);
651      }
652
653      /**
654       * RDF/XML ARP property: <c>embedding</c>.
655       *
656       * <p>
657       * Sets ARP to look for RDF embedded within an enclosing XML document.
658       *
659       * <h5 class='section'>See Also:</h5><ul>
660       *    <li>
661       *       {doc ext.ARP/ARPOptions.html#setEmbedding(boolean) ARPOptions.setEmbedding(boolean)}
662       * </ul>
663       *
664       * @param value
665       *    The new value for this property.
666       * @return This object.
667       */
668      public SELF rdfxml_embedding(boolean value) {
669         return jena("rdfXml.embedding", value);
670      }
671
672      /**
673       * RDF/XML ARP property: <c>error-mode</c>.
674       *
675       * <p>
676       * This allows a coarse-grained approach to control of error handling.
677       *
678       * <ul class='values'>
679       *    <li><js>"default"</js>
680       *    <li><js>"lax"</js>
681       *    <li><js>"strict"</js>
682       *    <li><js>"strict-ignore"</js>
683       *    <li><js>"strict-warning"</js>
684       *    <li><js>"strict-error"</js>
685       *    <li><js>"strict-fatal"</js>
686       * </ul>
687       *
688       * <h5 class='section'>See Also:</h5><ul>
689       *    <li>
690       *       {doc ext.ARP/ARPOptions.html#setDefaultErrorMode() ARPOptions.setDefaultErrorMode()}
691       *    <li>
692       *       {doc ext.ARP/ARPOptions.html#setLaxErrorMode() ARPOptions.setLaxErrorMode()}
693       *    <li>
694       *       {doc ext.ARP/ARPOptions.html#setStrictErrorMode() ARPOptions.setStrictErrorMode()}
695       *    <li>
696       *       {doc ext.ARP/ARPOptions.html#setStrictErrorMode(int) ARPOptions.setStrictErrorMode(int)}
697       * </ul>
698       *
699       * @param value
700       *    The new value for this property.
701       * @return This object.
702       */
703      public SELF rdfxml_errorMode(String value) {
704         return jena("rdfXml.error-mode", value);
705      }
706
707      /**
708       * RDF/XML property: <c>iri_rules</c>.
709       *
710       * <p>
711       * Set the engine for checking and resolving.
712       *
713       * <ul class='values spaced-list'>
714       *    <li>
715       *       <js>"lax"</js> - The rules for RDF URI references only, which does permit spaces although the use of spaces
716       *       is not good practice.
717       *    <li>
718       *       <js>"strict"</js> - Sets the IRI engine with rules for valid IRIs, XLink and RDF; it does not permit spaces
719       *       in IRIs.
720       *    <li>
721       *       <js>"iri"</js> - Sets the IRI engine to IRI
722       *       ({doc http://www.ietf.org/rfc/rfc3986.txt RFC 3986},
723       *       {doc http://www.ietf.org/rfc/rfc3987.txt RFC 3987}).
724       *
725       * </ul>
726       *
727       * @param value
728       *    The new value for this property.
729       * @return This object.
730       */
731      public SELF rdfxml_iriRules(String value) {
732         return jena("rdfXml.iri-rules", value);
733      }
734
735      /**
736       * RDF/XML property: <c>longId</c>.
737       *
738       * <p>
739       * Whether to use long ID's for anon resources.
740       * Short ID's are easier to read, but can run out of memory on very large models.
741       *
742       * @return This object.
743       */
744      public SELF rdfxml_longId() {
745         return rdfxml_longId(true);
746      }
747
748      /**
749       * RDF/XML property: <c>longId</c>.
750       *
751       * <p>
752       * Whether to use long ID's for anon resources.
753       * Short ID's are easier to read, but can run out of memory on very large models.
754       *
755       * @param value
756       *    The new value for this property.
757       * @return This object.
758       */
759      public SELF rdfxml_longId(boolean value) {
760         return jena("rdfXml.longId", value);
761      }
762
763      /**
764       * RDF/XML property: <c>relativeURIs</c>.
765       *
766       * <p>
767       * What sort of relative URIs should be used.
768       *
769       * <p>
770       * A comma separate list of options:
771       * <ul class='spaced-list'>
772       *    <li>
773       *       <js>"same-document"</js> - Same-document references (e.g. <js>""</js> or <js>"#foo"</js>)
774       *    <li>
775       *       <js>"network"</js>  - Network paths (e.g. <js>"//example.org/foo"</js> omitting the URI scheme)
776       *    <li>
777       *       <js>"absolute"</js> - Absolute paths (e.g. <js>"/foo"</js> omitting the scheme and authority)
778       *    <li>
779       *       <js>"relative"</js> - Relative path not beginning in <js>"../"</js>
780       *    <li>
781       *       <js>"parent"</js> - Relative path beginning in <js>"../"</js>
782       *    <li>
783       *       <js>"grandparent"</js> - Relative path beginning in <js>"../../"</js>
784       * </ul>
785       *
786       * <p>
787       * The default value is <js>"same-document, absolute, relative, parent"</js>.
788       * To switch off relative URIs use the value <js>""</js>.
789       * Relative URIs of any of these types are output where possible if and only if the option has been specified.
790       *
791       * @param value
792       *    The new value for this property.
793       * @return This object.
794       */
795      public SELF rdfxml_relativeUris(String value) {
796         return jena("rdfXml.relativeURIs", value);
797      }
798
799      /**
800       * RDF/XML property: <c>showXmlDeclaration</c>.
801       *
802       * <ul class='values spaced-list'>
803       *    <li>
804       *       <js>"true"</js> - Add XML Declaration to the output.
805       *    <li>
806       *       <js>"false"</js> - Don't add XML Declaration to the output.
807       *    <li>
808       *       <js>"default"</js> - Only add an XML Declaration when asked to write to an <c>OutputStreamWriter</c>
809       *       that uses some encoding other than <c>UTF-8</c> or <c>UTF-16</c>.
810       *       In this case the encoding is shown in the XML declaration.
811       * </ul>
812       *
813       * @param value
814       *    The new value for this property.
815       * @return This object.
816       */
817      public SELF rdfxml_showXmlDeclaration(String value) {
818         return jena("rdfXml.showXmlDeclaration", value);
819      }
820
821      /**
822       * RDF/XML property: <c>tab</c>.
823       *
824       * <p>
825       * The number of spaces with which to indent XML child elements.
826       *
827       * @param value
828       *    The new value for this property.
829       * @return This object.
830       */
831      public SELF rdfxml_tab(int value) {
832         return jena("rdfXml.tab", value);
833      }
834
835      /**
836       * RDF/XML property: <c>xmlbase</c>.
837       *
838       * <p>
839       * The value to be included for an <xa>xml:base</xa> attribute on the root element in the file.
840       *
841       * @param value
842       *    The new value for this property.
843       * @return This object.
844       */
845      public SELF rdfxml_xmlbase(String value) {
846         return jena("rdfXml.xmlbase", value);
847      }
848
849      /**
850       * Trim whitespace from text elements.
851       *
852       * <p>
853       * When enabled, whitespace in text elements will be automatically trimmed.
854       *
855       * <h5 class='section'>Example:</h5>
856       * <p class='bjava'>
857       *    <jc>// Create an RDF parser that trims whitespace.</jc>
858       *    ReaderParser <jv>parser</jv> = RdfParser
859       *       .<jsm>create</jsm>()
860       *       .xml()
861       *       .trimWhitespace()
862       *       .build();
863       * </p>
864       *
865       * @return This object.
866       */
867      public SELF trimWhitespace() {
868         return trimWhitespace(true);
869      }
870
871      /**
872       * Same as {@link #trimWhitespace()} but allows you to explicitly specify the value.
873       *
874       * @param value
875       *    The new value for this setting.
876       * @return This object.
877       */
878      public SELF trimWhitespace(boolean value) {
879         trimWhitespace = value;
880         return self();
881      }
882
883      /**
884       * Shortcut for calling <code>language(<jsf>LANG_TURTLE</jsf>)</code>.
885       *
886       * @return This object.
887       */
888      public SELF turtle() {
889         return language(Constants.LANG_TURTLE);
890      }
891
892      /**
893       * Shortcut for calling <code>language(<jsf>LANG_JSONLD</jsf>)</code>.
894       *
895       * @return This object.
896       */
897      public SELF jsonLd() {
898         return language(Constants.LANG_JSONLD);
899      }
900
901      /**
902       * Shortcut for calling <code>language(<jsf>LANG_NQUADS</jsf>)</code>.
903       *
904       * @return This object.
905       */
906      public SELF nQuads() {
907         return language(Constants.LANG_NQUADS);
908      }
909
910      /**
911       * Shortcut for calling <code>language(<jsf>LANG_TRIG</jsf>)</code>.
912       *
913       * @return This object.
914       */
915      public SELF triG() {
916         return language(Constants.LANG_TRIG);
917      }
918
919      /**
920       * Shortcut for calling <code>language(<jsf>LANG_TRIX</jsf>)</code>.
921       *
922       * @return This object.
923       */
924      public SELF triX() {
925         return language(Constants.LANG_TRIX);
926      }
927
928      /**
929       * Shortcut for calling <code>language(<jsf>LANG_RDFJSON</jsf>)</code>.
930       *
931       * @return This object.
932       */
933      public SELF rdfJson() {
934         return language(Constants.LANG_RDFJSON);
935      }
936
937
938      /**
939       * Shortcut for calling <code>language(<jsf>LANG_RDF_XML</jsf>)</code>.
940       *
941       * @return This object.
942       */
943      public SELF xml() {
944         return language(Constants.LANG_RDF_XML);
945      }
946
947      /**
948       * Shortcut for calling <code>language(<jsf>LANG_RDF_XML_ABBREV</jsf>)</code>.
949       *
950       * @return This object.
951       */
952      public SELF xmlabbrev() {
953         return language(Constants.LANG_RDF_XML_ABBREV);
954      }
955
956      SELF jena(String key, Object value) {
957         jenaSettings.put(key, value);
958         return self();
959      }
960   }
961
962   /**
963    * Concrete default builder leaf for the non-subclassed {@link RdfParser#create()} / {@link RdfParser#copy()} path.
964    */
965   public static final class DefaultBuilder extends Builder<DefaultBuilder> {
966
967      DefaultBuilder() {}
968
969      DefaultBuilder(RdfParser copyFrom) {
970         super(copyFrom);
971      }
972
973      DefaultBuilder(Builder<?> copyFrom) {
974         super(copyFrom);
975      }
976
977      @Override /* Overridden from Context.Builder<?> */
978      public DefaultBuilder copy() {
979         return new DefaultBuilder(this);
980      }
981   }
982
983   /**
984    * Creates a new builder for this object.
985    *
986    * @return A new builder.
987    */
988   @SuppressWarnings({
989      "java:S1452", // Builder<?> wildcard return intentional; callers chain via fluent API without needing the concrete type
990      "java:S9149" // Public Juneau DSL factory; hiding parent create() is intentional.
991   })
992   public static Builder<?> create() {
993      return new DefaultBuilder();
994   }
995
996   private static String getConsumes(Builder<?> builder) {
997      if (nn(builder.getConsumes()))
998         return builder.getConsumes();
999      return switch (builder.language) {
1000         case "RDF/XML", "RDF/XML-ABBREV" -> "text/xml+rdf";
1001         case "N-TRIPLE" -> "text/n-triple";
1002         case "N3" -> "text/n3";
1003         case "N3-PP" -> "text/n3-pp";
1004         case "N3-PLAIN" -> "text/n3-plain";
1005         case "N3-TRIPLES" -> "text/n3-triples";
1006         case "TURTLE" -> "text/turtle";
1007         case "JSON-LD" -> "application/ld+json";
1008         case "N-QUADS" -> "application/n-quads";
1009         case "TRIG" -> "application/trig";
1010         case "TRIX" -> "application/trix+xml";
1011         case "RDF/JSON" -> "application/rdf+json";
1012         default -> "text/xml+rdf";
1013      };
1014   }
1015
1016   protected final boolean looseCollections;
1017   protected final boolean trimWhitespace;
1018   protected final Namespace juneauBpNs;
1019   protected final Namespace juneauNs;
1020   protected final RdfCollectionFormat collectionFormat;
1021   protected final String language;
1022
1023   final Map<String,Object> jenaSettings;
1024   private final Map<ClassMeta<?>,RdfClassMeta> rdfClassMetas = new ConcurrentHashMap<>();
1025   private final Map<BeanMeta<?>,RdfBeanMeta> rdfBeanMetas = new ConcurrentHashMap<>();
1026
1027   private final Map<BeanPropertyMeta,RdfBeanPropertyMeta> rdfBeanPropertyMetas = new ConcurrentHashMap<>();
1028   private final Map<ClassMeta<?>,XmlClassMeta> xmlClassMetas = new ConcurrentHashMap<>();
1029   private final Map<BeanMeta<?>,XmlBeanMeta> xmlBeanMetas = new ConcurrentHashMap<>();
1030
1031   private final Map<BeanPropertyMeta,XmlBeanPropertyMeta> xmlBeanPropertyMetas = new ConcurrentHashMap<>();
1032
1033   /**
1034    * Constructor.
1035    *
1036    * @param builder The builder for this object.
1037    *    <br>Cannot be <jk>null</jk>.
1038    */
1039   public RdfParser(Builder<?> builder) {
1040      super(builder.consumes(getConsumes(builder)));
1041
1042      collectionFormat = builder.collectionFormat;
1043      juneauBpNs = builder.juneauBpNs;
1044      juneauNs = builder.juneauNs;
1045      language = builder.language;
1046      looseCollections = builder.looseCollections;
1047      trimWhitespace = builder.trimWhitespace;
1048      jenaSettings = new TreeMap<>(builder.jenaSettings);
1049   }
1050
1051   @Override /* Overridden from Context */
1052   public Builder<?> copy() {
1053      return new DefaultBuilder(this);
1054   }
1055
1056   @Override /* Overridden from Context */
1057   public RdfParserSession.Builder createSession() {
1058      return RdfParserSession.create(this);
1059   }
1060
1061   @Override /* Overridden from RdfMetaProvider */
1062   public RdfBeanMeta getRdfBeanMeta(BeanMeta<?> bm) {
1063      return rdfBeanMetas.computeIfAbsent(bm, k -> new RdfBeanMeta(k, this));
1064   }
1065
1066   @Override /* Overridden from RdfMetaProvider */
1067   public RdfBeanPropertyMeta getRdfBeanPropertyMeta(BeanPropertyMeta bpm) {
1068      return rdfBeanPropertyMetas.computeIfAbsent(bpm, k -> new RdfBeanPropertyMeta(k.getDelegateFor(), this));
1069   }
1070
1071   @Override /* Overridden from RdfMetaProvider */
1072   public RdfClassMeta getRdfClassMeta(ClassMeta<?> cm) {
1073      return rdfClassMetas.computeIfAbsent(cm, k -> new RdfClassMeta(k, this));
1074   }
1075
1076   @Override /* Overridden from Context */
1077   public RdfParserSession getSession() { return createSession().build(); }
1078
1079   @Override /* Overridden from XmlMetaProvider */
1080   public XmlBeanMeta getXmlBeanMeta(BeanMeta<?> bm) {
1081      return xmlBeanMetas.computeIfAbsent(bm, k -> new XmlBeanMeta(k, this));
1082   }
1083
1084   @Override /* Overridden from XmlMetaProvider */
1085   public XmlBeanPropertyMeta getXmlBeanPropertyMeta(BeanPropertyMeta bpm) {
1086      return xmlBeanPropertyMetas.computeIfAbsent(bpm, k -> new XmlBeanPropertyMeta(k.getDelegateFor(), this));
1087   }
1088
1089   @Override /* Overridden from XmlMetaProvider */
1090   public XmlClassMeta getXmlClassMeta(ClassMeta<?> cm) {
1091      return xmlClassMetas.computeIfAbsent(cm, k -> new XmlClassMeta(k, this));
1092   }
1093
1094   /**
1095    * RDF format for representing collections and arrays.
1096    *
1097    * @see RdfParser.Builder#collectionFormat(RdfCollectionFormat)
1098    * @return
1099    *    RDF format for representing collections and arrays.
1100    */
1101   protected final RdfCollectionFormat getCollectionFormat() { return collectionFormat; }
1102
1103   /**
1104    * All Jena-related configuration properties.
1105    *
1106    * @return
1107    *    A map of all Jena-related configuration properties.
1108    */
1109   protected final Map<String,Object> getJenaSettings() { return u(jenaSettings); }
1110
1111   /**
1112    * Default XML namespace for bean properties.
1113    *
1114    * @see RdfParser.Builder#juneauBpNs(Namespace)
1115    * @return
1116    *    Default XML namespace for bean properties.
1117    */
1118   protected final Namespace getJuneauBpNs() { return juneauBpNs; }
1119
1120   /**
1121    * XML namespace for Juneau properties.
1122    *
1123    * @see RdfParser.Builder#juneauNs(Namespace)
1124    * @return
1125    *    XML namespace for Juneau properties.
1126    */
1127   protected final Namespace getJuneauNs() { return juneauNs; }
1128
1129   /**
1130    * RDF language.
1131    *
1132    * @see RdfParser.Builder#language(String)
1133    * @return
1134    *    The RDF language to use.
1135    */
1136   protected final String getLanguage() { return language; }
1137
1138   /**
1139    * Collections should be serialized and parsed as loose collections.
1140    *
1141    * @see RdfParser.Builder#looseCollections()
1142    * @return
1143    *    <jk>true</jk> if collections of resources are handled as loose collections of resources in RDF instead of
1144    *    resources that are children of an RDF collection (e.g. Sequence, Bag).
1145    */
1146   protected final boolean isLooseCollections() { return looseCollections; }
1147
1148   /**
1149    * Trim whitespace from text elements.
1150    *
1151    * @see RdfParser.Builder#trimWhitespace()
1152    * @return
1153    *    <jk>true</jk> if whitespace in text elements will be automatically trimmed.
1154    */
1155   protected final boolean isTrimWhitespace() { return trimWhitespace; }
1156
1157   @Override /* Overridden from ReaderParser */
1158   protected FluentMap<String,Object> properties() {
1159      return super.properties()
1160         .a("collectionFormat", collectionFormat)
1161         .a("juneauBpNs", juneauBpNs)
1162         .a("juneauNs", juneauNs)
1163         .a("language", language)
1164         .a("looseCollections", looseCollections)
1165         .a("trimWhitespace", trimWhitespace);
1166   }
1167}