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.xml;
018
019import static org.apache.juneau.commons.utils.Shorts.*;
020import static org.apache.juneau.commons.utils.SystemUtils.*;
021import static org.apache.juneau.commons.utils.ThrowableUtils.*;
022
023import java.io.*;
024import java.util.*;
025import java.util.concurrent.*;
026import java.util.concurrent.atomic.*;
027
028import javax.xml.stream.*;
029import javax.xml.stream.util.*;
030
031import org.apache.juneau.commons.bean.*;
032import org.apache.juneau.commons.collections.*;
033import org.apache.juneau.marshall.*;
034import org.apache.juneau.marshall.collections.*;
035import org.apache.juneau.marshall.parser.*;
036import org.apache.juneau.marshall.stream.*;
037
038/**
039 * Parses text generated by the {@link XmlSerializer} class back into a POJO model.
040 *
041 * <h5 class='topic'>Media types</h5>
042 * <p>
043 * Handles <c>Content-Type</c> types:  <bc>text/xml</bc>
044 *
045 * <h5 class='topic'>Description</h5>
046 * <p>
047 * See the {@link XmlSerializer} class for a description of Juneau-generated XML.
048 *
049 * <h5 class='figure'>Example input (Map of name/age):</h5>
050 * <p class='bxml'>
051 *    <xt>&lt;object&gt;</xt>
052 *       <xt>&lt;name&gt;</xt>Alice<xt>&lt;/name&gt;</xt>
053 *       <xt>&lt;age&gt;</xt>30<xt>&lt;/age&gt;</xt>
054 *    <xt>&lt;/object&gt;</xt>
055 * </p>
056 *
057 * <h5 class='figure'>Complex (nested object + array):</h5>
058 * <p class='bxml'>
059 *    <xt>&lt;object&gt;</xt>
060 *       <xt>&lt;name&gt;</xt>Alice<xt>&lt;/name&gt;</xt>
061 *       <xt>&lt;age&gt;</xt>30<xt>&lt;/age&gt;</xt>
062 *       <xt>&lt;address&gt;</xt>
063 *          <xt>&lt;street&gt;</xt>123 Main St<xt>&lt;/street&gt;</xt>
064 *          <xt>&lt;city&gt;</xt>Boston<xt>&lt;/city&gt;</xt>
065 *          <xt>&lt;state&gt;</xt>MA<xt>&lt;/state&gt;</xt>
066 *       <xt>&lt;/address&gt;</xt>
067 *       <xt>&lt;tags&gt;</xt>
068 *          <xt>&lt;string&gt;</xt>a<xt>&lt;/string&gt;</xt>
069 *          <xt>&lt;string&gt;</xt>b<xt>&lt;/string&gt;</xt>
070 *          <xt>&lt;string&gt;</xt>c<xt>&lt;/string&gt;</xt>
071 *       <xt>&lt;/tags&gt;</xt>
072 *    <xt>&lt;/object&gt;</xt>
073 * </p>
074 *
075 * <h5 class='section'>Notes:</h5><ul>
076 *    <li class='note'>This class is thread safe and reusable.
077 * </ul>
078 *
079 * <h5 class='section'>See Also:</h5><ul>
080 *    <li class='link'><a class="doclink" href="https://juneau.apache.org/docs/topics/XmlSupport">XML</a>
081
082 * </ul>
083 */
084@SuppressWarnings({
085   "java:S1192", // Duplicated literals (argument/property names) read more clearly inline than as constants
086   "java:S9149", // Per-format static factories intentionally shadow the parent's.
087   "resource" // Closeable resources are owned by the caller's parser session; Eclipse JDT @Owning warning is by design.
088})
089public class XmlParser extends ReaderParser implements XmlMetaProvider, RecordReadable, ArrayRecordReadable {
090
091   /**
092    * Builder class.
093    */
094   @SuppressWarnings({
095      "java:S119" // 'SELF' (CRTP self-type) is intentional and clearer than a single-letter name.
096   })
097   public abstract static class Builder<SELF extends Builder<SELF>> extends ReaderParser.Builder<SELF> {
098
099      private static final Cache<HashKey,XmlParser> CACHE = Cache.of(HashKey.class, XmlParser.class).build();
100
101      private boolean preserveRootElement;
102      private boolean validating;
103      private Class<? extends XMLEventAllocator> eventAllocator;
104      private Class<? extends XMLReporter> reporter;
105      private Class<? extends XMLResolver> resolver;
106
107      /**
108       * Constructor, default settings.
109       */
110      protected Builder() {
111         consumes("text/xml,application/xml");
112         preserveRootElement = env("XmlParser.preserveRootElement", false);
113         validating = env("XmlParser.validating", false);
114         eventAllocator = null;
115         reporter = null;
116         resolver = null;
117      }
118
119      /**
120       * Copy constructor.
121       *
122       * @param copyFrom The builder to copy from.
123       *    <br>Cannot be <jk>null</jk>.
124       */
125      protected Builder(Builder<?> copyFrom) {
126         super(reqnn("copyFrom", copyFrom));
127         preserveRootElement = copyFrom.preserveRootElement;
128         validating = copyFrom.validating;
129         eventAllocator = copyFrom.eventAllocator;
130         reporter = copyFrom.reporter;
131         resolver = copyFrom.resolver;
132      }
133
134      /**
135       * Copy constructor.
136       *
137       * @param copyFrom The bean to copy from.
138       *    <br>Cannot be <jk>null</jk>.
139       */
140      protected Builder(XmlParser copyFrom) {
141         super(reqnn("copyFrom", copyFrom));
142         preserveRootElement = copyFrom.preserveRootElement;
143         validating = copyFrom.validating;
144         eventAllocator = copyFrom.eventAllocator;
145         reporter = copyFrom.reporter;
146         resolver = copyFrom.resolver;
147      }
148
149      @Override /* Overridden from Context.Builder<?> */
150      public XmlParser build() {
151         return cache(CACHE).build(XmlParser.class);
152      }
153
154      @Override /* Overridden from Context.Builder<?> */
155      public abstract SELF copy();
156
157      /**
158       * XML event allocator.
159       *
160       * <p>
161       * Associates an {@link XMLEventAllocator} with this parser.
162       *
163       * @param value The new value for this property.
164       *    <br>Can be <jk>null</jk> (no event allocator will be used).
165       * @return This object.
166       */
167      public SELF eventAllocator(Class<? extends XMLEventAllocator> value) {
168         eventAllocator = value;
169         return self();
170      }
171
172      @Override /* Overridden from Context.Builder<?> */
173      public HashKey hashKey() {
174         // @formatter:off
175         return HashKey.of(
176            super.hashKey(),
177            preserveRootElement,
178            validating,
179            eventAllocator,
180            reporter,
181            resolver
182         );
183         // @formatter:on
184      }
185
186      /**
187       * Preserve root element during generalized parsing.
188       *
189       * <p>
190       * When enabled, when parsing into a generic {@link JsonMap}, the map will contain a single entry whose key
191       * is the root element name.
192       *
193       * <h5 class='section'>Example:</h5>
194       * <p class='bjava'>
195       *    <jc>// Parser with preserve-root-element.</jc>
196       *    ReaderParser <jv>parser1</jv> = XmlParser
197       *       .<jsm>create</jsm>()
198       *       .preserveRootElement()
199       *       .build();
200       *
201       *    <jc>// Parser without preserve-root-element (the default behavior).</jc>
202       *    ReaderParser <jv>parser2</jv> = XmlParser
203       *       .<jsm>create</jsm>()
204       *       .build();
205       *
206       *    String <jv>xml</jv> = <js>"&lt;root&gt;&lt;a&gt;foobar&lt;/a&gt;&lt;/root&gt;"</js>;
207       *
208       *    <jc>// Produces:  "{ root: { a:'foobar' }}"</jc>
209       *    JsonMap <jv>map1</jv> = <jv>parser1</jv>.read(<jv>xml</jv>, JsonMap.<jk>class</jk>);
210       *
211       *    <jc>// Produces:  "{ a:'foobar' }"</jc>
212       *    JsonMap <jv>map2</jv> = <jv>parser2</jv>.read(<jv>xml</jv>, JsonMap.<jk>class</jk>);
213       * </p>
214       *
215       * @return This object.
216       */
217      public SELF preserveRootElement() {
218         return preserveRootElement(true);
219      }
220
221      /**
222       * Same as {@link #preserveRootElement()} but allows you to explicitly specify the value.
223       *
224       * @param value The value for this setting.
225       * @return This object.
226       */
227      public SELF preserveRootElement(boolean value) {
228         preserveRootElement = value;
229         return self();
230      }
231
232      /**
233       * XML reporter.
234       *
235       * <p>
236       * Associates an {@link XMLReporter} with this parser.
237       *
238       * @param value The new value for this property.
239       *    <br>Can be <jk>null</jk> (no reporter will be used).
240       * @return This object.
241       */
242      public SELF reporter(Class<? extends XMLReporter> value) {
243         reporter = value;
244         return self();
245      }
246
247      /**
248       * XML resolver.
249       *
250       * <p>
251       * Associates an {@link XMLResolver} with this parser.
252       *
253       * @param value The new value for this property.
254       *    <br>Can be <jk>null</jk> (no resolver will be used).
255       * @return This object.
256       */
257      public SELF resolver(Class<? extends XMLResolver> value) {
258         resolver = value;
259         return self();
260      }
261
262
263      /**
264       * Enable validation.
265       *
266       * <p>
267       * If <jk>true</jk>, XML document will be validated.
268       *
269       * <p>
270       * See {@link XMLInputFactory#IS_VALIDATING} for more info.
271       *
272       * @return This object.
273       */
274      public SELF validating() {
275         return validating(true);
276      }
277
278      /**
279       * Same as {@link #validating()} but allows you to explicitly specify the value.
280       *
281       * @param value The value for this setting.
282       * @return This object.
283       */
284      public SELF validating(boolean value) {
285         validating = value;
286         return self();
287      }
288   }
289
290   /**
291    * Concrete default builder leaf for the non-subclassed {@link XmlParser#create()} / {@link XmlParser#copy()} path.
292    */
293   @SuppressWarnings({
294      "java:S110" // Inheritance depth follows the parser builder chain; intentional layered design
295   })
296   public static final class DefaultBuilder extends Builder<DefaultBuilder> {
297
298      DefaultBuilder() {}
299
300      DefaultBuilder(XmlParser copyFrom) {
301         super(copyFrom);
302      }
303
304      DefaultBuilder(Builder<?> copyFrom) {
305         super(copyFrom);
306      }
307
308      @Override /* Overridden from Context.Builder<?> */
309      public DefaultBuilder copy() {
310         return new DefaultBuilder(this);
311      }
312   }
313
314   /** Default parser, all default settings.*/
315   public static final XmlParser DEFAULT = new XmlParser(create());
316
317   /**
318    * Creates a new builder for this object.
319    *
320    * @return A new builder.
321    */
322   @SuppressWarnings({
323      "java:S1452" // Builder<?> wildcard return intentional; callers chain via fluent API without needing the concrete type
324   })
325   public static Builder<?> create() {
326      return new DefaultBuilder();
327   }
328
329   protected final boolean preserveRootElement;
330   protected final boolean validating;
331   protected final Class<? extends XMLEventAllocator> eventAllocator;
332   protected final Class<? extends XMLReporter> reporter;
333   protected final Class<? extends XMLResolver> resolver;
334
335   private final XMLEventAllocator eventAllocatorImpl;
336   private final XMLReporter reporterImpl;
337   private final XMLResolver resolverImpl;
338   private final Map<BeanMeta<?>,XmlBeanMeta> xmlBeanMetas = new ConcurrentHashMap<>();
339   private final Map<BeanPropertyMeta,XmlBeanPropertyMeta> xmlBeanPropertyMetas = new ConcurrentHashMap<>();
340   private final Map<ClassMeta<?>,XmlClassMeta> xmlClassMetas = new ConcurrentHashMap<>();
341   private final AtomicReference<XMLInputFactory> validatingFactory = new AtomicReference<>();
342   private final AtomicReference<XMLInputFactory> nonValidatingFactory = new AtomicReference<>();
343
344   /**
345    * Constructor.
346    *
347    * @param builder
348    *    The property store containing all the settings for this object.
349    */
350   public XmlParser(Builder<?> builder) {
351      super(builder);
352      eventAllocator = builder.eventAllocator;
353      preserveRootElement = builder.preserveRootElement;
354      reporter = builder.reporter;
355      resolver = builder.resolver;
356      validating = builder.validating;
357
358      reporterImpl = nn(reporter) ? newInstance(reporter) : null;
359      resolverImpl = nn(resolver) ? newInstance(resolver) : null;
360      eventAllocatorImpl = nn(eventAllocator) ? newInstance(eventAllocator) : null;
361   }
362
363   @Override /* Overridden from Context */
364   public Builder<?> copy() {
365      return new DefaultBuilder(this);
366   }
367
368   @Override /* Overridden from Context */
369   public XmlParserSession.Builder<?> createSession() {
370      return XmlParserSession.create(this);
371   }
372
373   @Override /* Overridden from Context */
374   public XmlParserSession getSession() { return createSession().build(); }
375
376   /**
377    * Convenience delegator for the whole-value {@link RecordReader} using <b>default session
378    * arguments</b>.  The real implementation lives on {@link XmlParserSession#readRecords(Object)}.
379    *
380    * @param input The input.
381    * @return A new {@link RecordReader} cursor.
382    * @throws IOException If a problem occurred opening the underlying input.
383    */
384   @Override /* RecordReadable */
385   public RecordReader readRecords(Object input) throws IOException {
386      return getSession().readRecords(input);
387   }
388
389   /**
390    * Convenience delegator for the buffered array-element {@link RecordReader} using <b>default
391    * session arguments</b>.  The real implementation lives on
392    * {@link XmlParserSession#readArrayRecords(Object)}.
393    *
394    * @param input The input.
395    * @return A buffered {@link RecordReader}.
396    * @throws IOException If a problem occurred reading the input.
397    */
398   @Override /* ArrayRecordReadable */
399   public RecordReader readArrayRecords(Object input) throws IOException {
400      return getSession().readArrayRecords(input);
401   }
402
403   /**
404    * Convenience delegator for the buffered array-element {@link RecordReader} with a
405    * caller-specified root element name, using <b>default session arguments</b>.  The real
406    * implementation lives on {@link XmlParserSession#readArrayRecords(Object, String)}.
407    *
408    * @param input The input.
409    * @param rootElementName The expected root element wrapping the array.
410    * @return A buffered {@link RecordReader}.
411    * @throws IOException If a problem occurred reading the input.
412    */
413   @Override /* ArrayRecordReadable */
414   public RecordReader readArrayRecords(Object input, String rootElementName) throws IOException {
415      return getSession().readArrayRecords(input, rootElementName);
416   }
417
418   /**
419    * The XML record cursor is buffered/{@link RecordAdapter}-backed, not O(1) streaming.
420    *
421    * @return Always <jk>false</jk>.
422    */
423   @Override /* RecordReadable */
424   public boolean isRecordStreaming() { return false; }
425
426   /**
427    * The XML array-record cursor is buffered/{@link RecordAdapter}-backed, not O(1) streaming.
428    *
429    * @return Always <jk>false</jk>.
430    */
431   @Override /* ArrayRecordReadable */
432   public boolean isArrayRecordStreaming() { return false; }
433
434   @Override /* Overridden from XmlMetaProvider */
435   public XmlBeanMeta getXmlBeanMeta(BeanMeta<?> bm) {
436      return xmlBeanMetas.computeIfAbsent(bm, k -> new XmlBeanMeta(k, this));
437   }
438
439   @Override /* Overridden from XmlMetaProvider */
440   public XmlBeanPropertyMeta getXmlBeanPropertyMeta(BeanPropertyMeta bpm) {
441      return xmlBeanPropertyMetas.computeIfAbsent(bpm, k -> new XmlBeanPropertyMeta(k.getDelegateFor(), this));
442   }
443
444   @Override /* Overridden from XmlMetaProvider */
445   public XmlClassMeta getXmlClassMeta(ClassMeta<?> cm) {
446      return xmlClassMetas.computeIfAbsent(cm, k -> new XmlClassMeta(k, this));
447   }
448
449   private static <T> T newInstance(Class<T> c) {
450      try {
451         return c.getDeclaredConstructor().newInstance();
452      } catch (Exception e) {
453         throw toRex(e);
454      }
455   }
456
457   /**
458    * XML event allocator.
459    *
460    * @see Builder#eventAllocator(Class)
461    * @return
462    *    The {@link XMLEventAllocator} associated with this parser, or <jk>null</jk> if there isn't one.
463    */
464   protected final XMLEventAllocator getEventAllocator() { return eventAllocatorImpl; }
465
466   /**
467    * XML reporter.
468    *
469    * @see Builder#reporter(Class)
470    * @return
471    *    The {@link XMLReporter} associated with this parser, or <jk>null</jk> if there isn't one.
472    */
473   protected final XMLReporter getReporter() { return reporterImpl; }
474
475   /**
476    * XML resolver.
477    *
478    * @see Builder#resolver(Class)
479    * @return
480    *    The {@link XMLResolver} associated with this parser, or <jk>null</jk> if there isn't one.
481    */
482   protected final XMLResolver getResolver() { return resolverImpl; }
483
484   /**
485    * Returns the input factory used to create stream readers for this parser.
486    *
487    * <p>
488    * The factory is built once per validating setting and then reused when the StAX implementation is known to be
489    * thread-safe after configuration.  Otherwise a new factory is returned on every call.
490    *
491    * @param validating The validating setting to configure the factory with.
492    * @return A configured factory.  Never <jk>null</jk>.
493    */
494   XMLInputFactory getInputFactory(boolean validating) {
495      var ref = validating ? validatingFactory : nonValidatingFactory;
496      var f = ref.get();
497      if (nn(f))
498         return f;
499      f = XmlReader.newFactory(validating, reporterImpl, resolverImpl, eventAllocatorImpl);
500      if (XmlReader.isShareable())
501         ref.set(f);
502      return f;
503   }
504
505   /**
506    * Preserve root element during generalized parsing.
507    *
508    * @see Builder#preserveRootElement()
509    * @return
510    *    <jk>true</jk> if when parsing into a generic {@link JsonMap}, the map will contain a single entry whose key
511    *    is the root element name.
512    */
513   protected final boolean isPreserveRootElement() { return preserveRootElement; }
514
515   /**
516    * Enable validation.
517    *
518    * @see Builder#validating()
519    * @return
520    *    <jk>true</jk> if XML document will be validated.
521    */
522   protected final boolean isValidating() { return validating; }
523
524   @Override /* Overridden from ReaderParser */
525   protected FluentMap<String,Object> properties() {
526      return super.properties()
527         .a("eventAllocator", eventAllocator)
528         .a("preserveRootElement", preserveRootElement)
529         .a("reporter", reporter)
530         .a("resolver", resolver)
531         .a("validating", validating);
532   }
533}