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><object></xt> 052 * <xt><name></xt>Alice<xt></name></xt> 053 * <xt><age></xt>30<xt></age></xt> 054 * <xt></object></xt> 055 * </p> 056 * 057 * <h5 class='figure'>Complex (nested object + array):</h5> 058 * <p class='bxml'> 059 * <xt><object></xt> 060 * <xt><name></xt>Alice<xt></name></xt> 061 * <xt><age></xt>30<xt></age></xt> 062 * <xt><address></xt> 063 * <xt><street></xt>123 Main St<xt></street></xt> 064 * <xt><city></xt>Boston<xt></city></xt> 065 * <xt><state></xt>MA<xt></state></xt> 066 * <xt></address></xt> 067 * <xt><tags></xt> 068 * <xt><string></xt>a<xt></string></xt> 069 * <xt><string></xt>b<xt></string></xt> 070 * <xt><string></xt>c<xt></string></xt> 071 * <xt></tags></xt> 072 * <xt></object></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>"<root><a>foobar</a></root>"</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}