1 /* 2 * Copyright (c) 1997, 2017, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package com.sun.xml.internal.bind.v2.runtime; 27 28 import java.io.IOException; 29 import java.lang.ref.WeakReference; 30 import java.lang.reflect.Field; 31 import java.lang.reflect.Method; 32 import java.lang.reflect.Type; 33 import java.util.Arrays; 34 import java.util.Collection; 35 import java.util.Collections; 36 import java.util.Comparator; 37 import java.util.HashMap; 38 import java.util.HashSet; 39 import java.util.LinkedHashMap; 40 import java.util.List; 41 import java.util.Map; 42 import java.util.Map.Entry; 43 import java.util.Set; 44 import java.util.TreeSet; 45 import javax.xml.bind.Binder; 46 import javax.xml.bind.JAXBContext; 47 import javax.xml.bind.JAXBElement; 48 import javax.xml.bind.JAXBException; 49 import javax.xml.bind.JAXBIntrospector; 50 import javax.xml.bind.Marshaller; 51 import javax.xml.bind.SchemaOutputResolver; 52 import javax.xml.bind.Unmarshaller; 53 import javax.xml.bind.Validator; 54 import javax.xml.bind.annotation.XmlAttachmentRef; 55 import javax.xml.bind.annotation.XmlList; 56 import javax.xml.bind.annotation.XmlNs; 57 import javax.xml.bind.annotation.XmlSchema; 58 import javax.xml.bind.annotation.adapters.XmlJavaTypeAdapter; 59 import javax.xml.namespace.QName; 60 import javax.xml.parsers.DocumentBuilder; 61 import javax.xml.parsers.DocumentBuilderFactory; 62 import javax.xml.parsers.FactoryConfigurationError; 63 import javax.xml.parsers.ParserConfigurationException; 64 import javax.xml.transform.Result; 65 import javax.xml.transform.Transformer; 66 import javax.xml.transform.TransformerConfigurationException; 67 import javax.xml.transform.TransformerFactory; 68 import javax.xml.transform.sax.SAXTransformerFactory; 69 import javax.xml.transform.sax.TransformerHandler; 70 71 import com.sun.istack.internal.NotNull; 72 import com.sun.istack.internal.Pool; 73 import com.sun.xml.internal.bind.v2.WellKnownNamespace; 74 import com.sun.xml.internal.bind.api.AccessorException; 75 import com.sun.xml.internal.bind.api.Bridge; 76 import com.sun.xml.internal.bind.api.BridgeContext; 77 import com.sun.xml.internal.bind.api.CompositeStructure; 78 import com.sun.xml.internal.bind.api.ErrorListener; 79 import com.sun.xml.internal.bind.api.JAXBRIContext; 80 import com.sun.xml.internal.bind.api.RawAccessor; 81 import com.sun.xml.internal.bind.api.TypeReference; 82 import com.sun.xml.internal.bind.unmarshaller.DOMScanner; 83 import com.sun.xml.internal.bind.util.Which; 84 import com.sun.xml.internal.bind.v2.model.annotation.RuntimeAnnotationReader; 85 import com.sun.xml.internal.bind.v2.model.annotation.RuntimeInlineAnnotationReader; 86 import com.sun.xml.internal.bind.v2.model.core.Adapter; 87 import com.sun.xml.internal.bind.v2.model.core.NonElement; 88 import com.sun.xml.internal.bind.v2.model.core.Ref; 89 import com.sun.xml.internal.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl; 90 import com.sun.xml.internal.bind.v2.model.impl.RuntimeModelBuilder; 91 import com.sun.xml.internal.bind.v2.model.nav.Navigator; 92 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeArrayInfo; 93 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeBuiltinLeafInfo; 94 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeClassInfo; 95 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeElementInfo; 96 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeEnumLeafInfo; 97 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeLeafInfo; 98 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeTypeInfo; 99 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeTypeInfoSet; 100 import com.sun.xml.internal.bind.v2.runtime.output.Encoded; 101 import com.sun.xml.internal.bind.v2.runtime.property.AttributeProperty; 102 import com.sun.xml.internal.bind.v2.runtime.property.Property; 103 import com.sun.xml.internal.bind.v2.runtime.reflect.Accessor; 104 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader; 105 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.TagName; 106 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl; 107 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext; 108 import com.sun.xml.internal.bind.v2.schemagen.XmlSchemaGenerator; 109 import com.sun.xml.internal.bind.v2.util.EditDistance; 110 import com.sun.xml.internal.bind.v2.util.QNameMap; 111 import com.sun.xml.internal.bind.v2.util.XmlFactory; 112 import com.sun.xml.internal.txw2.output.ResultFactory; 113 114 import org.w3c.dom.Document; 115 import org.w3c.dom.Element; 116 import org.w3c.dom.Node; 117 import org.xml.sax.SAXException; 118 import org.xml.sax.SAXParseException; 119 120 /** 121 * This class provides the implementation of JAXBContext. 122 * 123 */ 124 public final class JAXBContextImpl extends JAXBRIContext { 125 126 /** 127 * All the bridge classes. 128 */ 129 private final Map<TypeReference,Bridge> bridges = new LinkedHashMap<TypeReference,Bridge>(); 130 131 /** 132 * Shared instance of {@link DocumentBuilder}. 133 * Lock before use. Lazily created. 134 */ 135 private static DocumentBuilder db; 136 137 private final QNameMap<JaxBeanInfo> rootMap = new QNameMap<JaxBeanInfo>(); 138 private final HashMap<QName,JaxBeanInfo> typeMap = new HashMap<QName,JaxBeanInfo>(); 139 140 /** 141 * Map from JAXB-bound {@link Class} to its {@link JaxBeanInfo}. 142 */ 143 private final Map<Class,JaxBeanInfo> beanInfoMap = new LinkedHashMap<Class,JaxBeanInfo>(); 144 145 /** 146 * All created {@link JaxBeanInfo}s. 147 * Updated from each {@link JaxBeanInfo}s constructors to avoid infinite recursion 148 * for a cyclic reference. 149 * 150 * <p> 151 * This map is only used while the {@link JAXBContextImpl} is built and set to null 152 * to avoid keeping references too long. 153 */ 154 protected Map<RuntimeTypeInfo,JaxBeanInfo> beanInfos = new LinkedHashMap<RuntimeTypeInfo, JaxBeanInfo>(); 155 156 private final Map<Class/*scope*/,Map<QName,ElementBeanInfoImpl>> elements = new LinkedHashMap<Class, Map<QName, ElementBeanInfoImpl>>(); 157 158 /** 159 * Pool of {@link Marshaller}s. 160 */ 161 public final Pool<Marshaller> marshallerPool = new Pool.Impl<Marshaller>() { 162 protected @NotNull Marshaller create() { 163 return createMarshaller(); 164 } 165 }; 166 167 public final Pool<Unmarshaller> unmarshallerPool = new Pool.Impl<Unmarshaller>() { 168 protected @NotNull Unmarshaller create() { 169 return createUnmarshaller(); 170 } 171 }; 172 173 /** 174 * Used to assign indices to known names in this grammar. 175 * Reset to null once the build phase is completed. 176 */ 177 public NameBuilder nameBuilder = new NameBuilder(); 178 179 /** 180 * Keeps the list of known names. 181 * This field is set once the build pahse is completed. 182 */ 183 public final NameList nameList; 184 185 /** 186 * Input to the JAXBContext.newInstance, so that we can recreate 187 * {@link RuntimeTypeInfoSet} whenever we need. 188 */ 189 private final String defaultNsUri; 190 private final Class[] classes; 191 192 /** 193 * true to reorder attributes lexicographically in preparation of the c14n support. 194 */ 195 protected final boolean c14nSupport; 196 197 /** 198 * Flag that user has provided a custom AccessorFactory for JAXB to use 199 */ 200 public final boolean xmlAccessorFactorySupport; 201 202 /** 203 * @see JAXBRIContext#TREAT_EVERYTHING_NILLABLE 204 */ 205 public final boolean allNillable; 206 207 /** 208 * Store properties, so that they can be recovered in the run (is here because of JSON encoding of Jersey). 209 */ 210 public final boolean retainPropertyInfo; 211 212 /** 213 * Suppress reflection accessor warnings. 214 */ 215 public final boolean supressAccessorWarnings; 216 217 /** 218 * Improved xsi type handling. 219 */ 220 public final boolean improvedXsiTypeHandling; 221 222 /** 223 * Disable security processing. 224 */ 225 public final boolean disableSecurityProcessing; 226 227 private WeakReference<RuntimeTypeInfoSet> typeInfoSetCache; 228 229 private @NotNull RuntimeAnnotationReader annotationReader; 230 231 private /*almost final*/ boolean hasSwaRef; 232 private final @NotNull Map<Class,Class> subclassReplacements; 233 234 /** 235 * If true, we aim for faster {@link JAXBContext} instantiation performance, 236 * instead of going after efficient sustained unmarshalling/marshalling performance. 237 * 238 * @since 2.0.4 239 */ 240 public final boolean fastBoot; 241 242 private Set<XmlNs> xmlNsSet = null; 243 244 /** 245 * If true, despite the specification, unmarshall child element with parent namespace, if child namespace is not specified. 246 * The default value is null for System {code}com.sun.xml.internal.bind.backupWithParentNamespace{code} property to be used, 247 * and false is assumed if it's not set either. 248 * 249 * Boolean 250 * @since 2.3.0 251 */ 252 public Boolean backupWithParentNamespace = null; 253 254 /** 255 * Returns declared XmlNs annotations (from package-level annotation XmlSchema 256 * 257 * @return set of all present XmlNs annotations 258 */ 259 public Set<XmlNs> getXmlNsSet() { 260 return xmlNsSet; 261 } 262 263 private JAXBContextImpl(JAXBContextBuilder builder) throws JAXBException { 264 265 this.defaultNsUri = builder.defaultNsUri; 266 this.retainPropertyInfo = builder.retainPropertyInfo; 267 this.annotationReader = builder.annotationReader; 268 this.subclassReplacements = builder.subclassReplacements; 269 this.c14nSupport = builder.c14nSupport; 270 this.classes = builder.classes; 271 this.xmlAccessorFactorySupport = builder.xmlAccessorFactorySupport; 272 this.allNillable = builder.allNillable; 273 this.supressAccessorWarnings = builder.supressAccessorWarnings; 274 this.improvedXsiTypeHandling = builder.improvedXsiTypeHandling; 275 this.disableSecurityProcessing = builder.disableSecurityProcessing; 276 this.backupWithParentNamespace = builder.backupWithParentNamespace; 277 278 Collection<TypeReference> typeRefs = builder.typeRefs; 279 280 boolean fastB; 281 try { 282 fastB = Boolean.getBoolean(JAXBContextImpl.class.getName()+".fastBoot"); 283 } catch (SecurityException e) { 284 fastB = false; 285 } 286 this.fastBoot = fastB; 287 288 RuntimeTypeInfoSet typeSet = getTypeInfoSet(); 289 290 // at least prepare the empty table so that we don't have to check for null later 291 elements.put(null,new LinkedHashMap<QName, ElementBeanInfoImpl>()); 292 293 // recognize leaf bean infos 294 for( RuntimeBuiltinLeafInfo leaf : RuntimeBuiltinLeafInfoImpl.builtinBeanInfos ) { 295 LeafBeanInfoImpl<?> bi = new LeafBeanInfoImpl(this,leaf); 296 beanInfoMap.put(leaf.getClazz(),bi); 297 for( QName t : bi.getTypeNames() ) 298 typeMap.put(t,bi); 299 } 300 301 for (RuntimeEnumLeafInfo e : typeSet.enums().values()) { 302 JaxBeanInfo<?> bi = getOrCreate(e); 303 for (QName qn : bi.getTypeNames()) 304 typeMap.put( qn, bi ); 305 if(e.isElement()) 306 rootMap.put( e.getElementName(), bi ); 307 } 308 309 for (RuntimeArrayInfo a : typeSet.arrays().values()) { 310 JaxBeanInfo<?> ai = getOrCreate(a); 311 for (QName qn : ai.getTypeNames()) 312 typeMap.put( qn, ai ); 313 } 314 315 for( Entry<Class, ? extends RuntimeClassInfo> e : typeSet.beans().entrySet() ) { 316 ClassBeanInfoImpl<?> bi = getOrCreate(e.getValue()); 317 318 XmlSchema xs = this.annotationReader.getPackageAnnotation(XmlSchema.class, e.getKey(), null); 319 if(xs != null) { 320 if(xs.xmlns() != null && xs.xmlns().length > 0) { 321 if(xmlNsSet == null) 322 xmlNsSet = new HashSet<XmlNs>(); 323 xmlNsSet.addAll(Arrays.asList(xs.xmlns())); 324 } 325 } 326 327 if(bi.isElement()) 328 rootMap.put( e.getValue().getElementName(), bi ); 329 330 for (QName qn : bi.getTypeNames()) 331 typeMap.put( qn, bi ); 332 } 333 334 // fill in element mappings 335 for( RuntimeElementInfo n : typeSet.getAllElements() ) { 336 ElementBeanInfoImpl bi = getOrCreate(n); 337 if(n.getScope()==null) 338 rootMap.put(n.getElementName(),bi); 339 340 RuntimeClassInfo scope = n.getScope(); 341 Class scopeClazz = scope==null?null:scope.getClazz(); 342 Map<QName,ElementBeanInfoImpl> m = elements.get(scopeClazz); 343 if(m==null) { 344 m = new LinkedHashMap<QName, ElementBeanInfoImpl>(); 345 elements.put(scopeClazz,m); 346 } 347 m.put(n.getElementName(),bi); 348 } 349 350 // this one is so that we can handle plain JAXBElements. 351 beanInfoMap.put(JAXBElement.class,new ElementBeanInfoImpl(this)); 352 // another special BeanInfoImpl just for marshalling 353 beanInfoMap.put(CompositeStructure.class,new CompositeStructureBeanInfo(this)); 354 355 getOrCreate(typeSet.getAnyTypeInfo()); 356 357 // then link them all! 358 for (JaxBeanInfo bi : beanInfos.values()) 359 bi.link(this); 360 361 // register primitives for boxed types just to make GrammarInfo fool-proof 362 for( Map.Entry<Class,Class> e : RuntimeUtil.primitiveToBox.entrySet() ) 363 beanInfoMap.put( e.getKey(), beanInfoMap.get(e.getValue()) ); 364 365 // build bridges 366 Navigator<Type, Class, Field, Method> nav = typeSet.getNavigator(); 367 368 for (TypeReference tr : typeRefs) { 369 XmlJavaTypeAdapter xjta = tr.get(XmlJavaTypeAdapter.class); 370 Adapter<Type,Class> a=null; 371 XmlList xl = tr.get(XmlList.class); 372 373 // eventually compute the in-memory type 374 Class erasedType = (Class) nav.erasure(tr.type); 375 376 if(xjta!=null) { 377 a = new Adapter<Type,Class>(xjta.value(),nav); 378 } 379 if(tr.get(XmlAttachmentRef.class)!=null) { 380 a = new Adapter<Type,Class>(SwaRefAdapter.class,nav); 381 hasSwaRef = true; 382 } 383 384 if(a!=null) { 385 erasedType = (Class) nav.erasure(a.defaultType); 386 } 387 388 Name name = nameBuilder.createElementName(tr.tagName); 389 390 InternalBridge bridge; 391 if(xl==null) 392 bridge = new BridgeImpl(this, name,getBeanInfo(erasedType,true),tr); 393 else 394 bridge = new BridgeImpl(this, name,new ValueListBeanInfoImpl(this,erasedType),tr); 395 396 if(a!=null) 397 bridge = new BridgeAdapter(bridge,a.adapterType); 398 399 bridges.put(tr,bridge); 400 } 401 402 this.nameList = nameBuilder.conclude(); 403 404 for (JaxBeanInfo bi : beanInfos.values()) 405 bi.wrapUp(); 406 407 // no use for them now 408 nameBuilder = null; 409 beanInfos = null; 410 } 411 412 /** 413 * True if this JAXBContext has {@link XmlAttachmentRef}. 414 */ 415 public boolean hasSwaRef() { 416 return hasSwaRef; 417 } 418 419 public RuntimeTypeInfoSet getRuntimeTypeInfoSet() { 420 try { 421 return getTypeInfoSet(); 422 } catch (IllegalAnnotationsException e) { 423 // impossible, once the model is constructred 424 throw new AssertionError(e); 425 } 426 } 427 428 /** 429 * Creates a {@link RuntimeTypeInfoSet}. 430 */ 431 public RuntimeTypeInfoSet getTypeInfoSet() throws IllegalAnnotationsException { 432 433 // check cache 434 if(typeInfoSetCache!=null) { 435 RuntimeTypeInfoSet r = typeInfoSetCache.get(); 436 if(r!=null) 437 return r; 438 } 439 440 final RuntimeModelBuilder builder = new RuntimeModelBuilder(this,annotationReader,subclassReplacements,defaultNsUri); 441 442 IllegalAnnotationsException.Builder errorHandler = new IllegalAnnotationsException.Builder(); 443 builder.setErrorHandler(errorHandler); 444 445 for( Class c : classes ) { 446 if(c==CompositeStructure.class) 447 // CompositeStructure doesn't have TypeInfo, so skip it. 448 // We'll add JaxBeanInfo for this later automatically 449 continue; 450 builder.getTypeInfo(new Ref<Type,Class>(c)); 451 } 452 453 this.hasSwaRef |= builder.hasSwaRef; 454 RuntimeTypeInfoSet r = builder.link(); 455 456 errorHandler.check(); 457 assert r!=null : "if no error was reported, the link must be a success"; 458 459 typeInfoSetCache = new WeakReference<RuntimeTypeInfoSet>(r); 460 461 return r; 462 } 463 464 465 public ElementBeanInfoImpl getElement(Class scope, QName name) { 466 Map<QName,ElementBeanInfoImpl> m = elements.get(scope); 467 if(m!=null) { 468 ElementBeanInfoImpl bi = m.get(name); 469 if(bi!=null) 470 return bi; 471 } 472 m = elements.get(null); 473 return m.get(name); 474 } 475 476 477 478 479 480 private ElementBeanInfoImpl getOrCreate( RuntimeElementInfo rei ) { 481 JaxBeanInfo bi = beanInfos.get(rei); 482 if(bi!=null) return (ElementBeanInfoImpl)bi; 483 484 // all elements share the same type, so we can't register them to beanInfoMap 485 return new ElementBeanInfoImpl(this, rei); 486 } 487 488 protected JaxBeanInfo getOrCreate( RuntimeEnumLeafInfo eli ) { 489 JaxBeanInfo bi = beanInfos.get(eli); 490 if(bi!=null) return bi; 491 bi = new LeafBeanInfoImpl(this,eli); 492 beanInfoMap.put(bi.jaxbType,bi); 493 return bi; 494 } 495 496 protected ClassBeanInfoImpl getOrCreate( RuntimeClassInfo ci ) { 497 ClassBeanInfoImpl bi = (ClassBeanInfoImpl)beanInfos.get(ci); 498 if(bi!=null) return bi; 499 bi = new ClassBeanInfoImpl(this,ci); 500 beanInfoMap.put(bi.jaxbType,bi); 501 return bi; 502 } 503 504 protected JaxBeanInfo getOrCreate( RuntimeArrayInfo ai ) { 505 JaxBeanInfo abi = beanInfos.get(ai); 506 if(abi!=null) return abi; 507 508 abi = new ArrayBeanInfoImpl(this,ai); 509 510 beanInfoMap.put(ai.getType(),abi); 511 return abi; 512 } 513 514 public JaxBeanInfo getOrCreate(RuntimeTypeInfo e) { 515 if(e instanceof RuntimeElementInfo) 516 return getOrCreate((RuntimeElementInfo)e); 517 if(e instanceof RuntimeClassInfo) 518 return getOrCreate((RuntimeClassInfo)e); 519 if(e instanceof RuntimeLeafInfo) { 520 JaxBeanInfo bi = beanInfos.get(e); // must have been created 521 assert bi!=null; 522 return bi; 523 } 524 if(e instanceof RuntimeArrayInfo) 525 return getOrCreate((RuntimeArrayInfo)e); 526 if(e.getType()==Object.class) { 527 // anyType 528 JaxBeanInfo bi = beanInfoMap.get(Object.class); 529 if(bi==null) { 530 bi = new AnyTypeBeanInfo(this,e); 531 beanInfoMap.put(Object.class,bi); 532 } 533 return bi; 534 } 535 536 throw new IllegalArgumentException(); 537 } 538 539 /** 540 * Gets the {@link JaxBeanInfo} object that can handle 541 * the given JAXB-bound object. 542 * 543 * <p> 544 * This method traverses the base classes of the given object. 545 * 546 * @return null 547 * if {@code c} isn't a JAXB-bound class and {@code fatal==false}. 548 */ 549 public final JaxBeanInfo getBeanInfo(Object o) { 550 // don't allow xs:anyType beanInfo to handle all the unbound objects 551 for( Class c=o.getClass(); c!=Object.class; c=c.getSuperclass()) { 552 JaxBeanInfo bi = beanInfoMap.get(c); 553 if(bi!=null) return bi; 554 } 555 if(o instanceof Element) 556 return beanInfoMap.get(Object.class); // return the BeanInfo for xs:anyType 557 for( Class c : o.getClass().getInterfaces()) { 558 JaxBeanInfo bi = beanInfoMap.get(c); 559 if(bi!=null) return bi; 560 } 561 return null; 562 } 563 564 /** 565 * Gets the {@link JaxBeanInfo} object that can handle 566 * the given JAXB-bound object. 567 * 568 * @param fatal 569 * if true, the failure to look up will throw an exception. 570 * Otherwise it will just return null. 571 */ 572 public final JaxBeanInfo getBeanInfo(Object o,boolean fatal) throws JAXBException { 573 JaxBeanInfo bi = getBeanInfo(o); 574 if(bi!=null) return bi; 575 if(fatal) { 576 if(o instanceof Document) 577 throw new JAXBException(Messages.ELEMENT_NEEDED_BUT_FOUND_DOCUMENT.format(o.getClass())); 578 throw new JAXBException(Messages.UNKNOWN_CLASS.format(o.getClass())); 579 } 580 return null; 581 } 582 583 /** 584 * Gets the {@link JaxBeanInfo} object that can handle 585 * the given JAXB-bound class. 586 * 587 * <p> 588 * This method doesn't look for base classes. 589 * 590 * @return null 591 * if {@code c} isn't a JAXB-bound class and {@code fatal==false}. 592 */ 593 public final <T> JaxBeanInfo<T> getBeanInfo(Class<T> clazz) { 594 return (JaxBeanInfo<T>)beanInfoMap.get(clazz); 595 } 596 597 /** 598 * Gets the {@link JaxBeanInfo} object that can handle 599 * the given JAXB-bound class. 600 * 601 * @param fatal 602 * if true, the failure to look up will throw an exception. 603 * Otherwise it will just return null. 604 */ 605 public final <T> JaxBeanInfo<T> getBeanInfo(Class<T> clazz,boolean fatal) throws JAXBException { 606 JaxBeanInfo<T> bi = getBeanInfo(clazz); 607 if(bi!=null) return bi; 608 if(fatal) 609 throw new JAXBException(clazz.getName()+" is not known to this context"); 610 return null; 611 } 612 613 /** 614 * Based on the tag name, determine what object to unmarshal, 615 * and then set a new object and its loader to the current unmarshaller state. 616 * 617 * @return 618 * null if the given name pair is not recognized. 619 */ 620 public final Loader selectRootLoader( UnmarshallingContext.State state, TagName tag ) { 621 JaxBeanInfo beanInfo = rootMap.get(tag.uri,tag.local); 622 if(beanInfo==null) 623 return null; 624 625 return beanInfo.getLoader(this,true); 626 } 627 628 /** 629 * Gets the {@link JaxBeanInfo} for the given named XML Schema type. 630 * 631 * @return 632 * null if the type name is not recognized. For schema 633 * languages other than XML Schema, this method always 634 * returns null. 635 */ 636 public JaxBeanInfo getGlobalType(QName name) { 637 return typeMap.get(name); 638 } 639 640 /** 641 * Finds a type name that this context recognizes which is 642 * "closest" to the given type name. 643 * 644 * <p> 645 * This method is used for error recovery. 646 */ 647 public String getNearestTypeName(QName name) { 648 String[] all = new String[typeMap.size()]; 649 int i=0; 650 for (QName qn : typeMap.keySet()) { 651 if(qn.getLocalPart().equals(name.getLocalPart())) 652 return qn.toString(); // probably a match, as people often gets confused about namespace. 653 all[i++] = qn.toString(); 654 } 655 656 String nearest = EditDistance.findNearest(name.toString(), all); 657 658 if(EditDistance.editDistance(nearest,name.toString())>10) 659 return null; // too far apart. 660 661 return nearest; 662 } 663 664 /** 665 * Returns the set of valid root tag names. 666 * For diagnostic use. 667 */ 668 public Set<QName> getValidRootNames() { 669 Set<QName> r = new TreeSet<QName>(QNAME_COMPARATOR); 670 for (QNameMap.Entry e : rootMap.entrySet()) { 671 r.add(e.createQName()); 672 } 673 return r; 674 } 675 676 /** 677 * Cache of UTF-8 encoded local names to improve the performance for the marshalling. 678 */ 679 private Encoded[] utf8nameTable; 680 681 public synchronized Encoded[] getUTF8NameTable() { 682 if(utf8nameTable==null) { 683 Encoded[] x = new Encoded[nameList.localNames.length]; 684 for( int i=0; i<x.length; i++ ) { 685 Encoded e = new Encoded(nameList.localNames[i]); 686 e.compact(); 687 x[i] = e; 688 } 689 utf8nameTable = x; 690 } 691 return utf8nameTable; 692 } 693 694 public int getNumberOfLocalNames() { 695 return nameList.localNames.length; 696 } 697 698 public int getNumberOfElementNames() { 699 return nameList.numberOfElementNames; 700 } 701 702 public int getNumberOfAttributeNames() { 703 return nameList.numberOfAttributeNames; 704 } 705 706 /** 707 * Creates a new identity transformer. 708 */ 709 static Transformer createTransformer(boolean disableSecureProcessing) { 710 try { 711 SAXTransformerFactory tf = (SAXTransformerFactory)XmlFactory.createTransformerFactory(disableSecureProcessing); 712 return tf.newTransformer(); 713 } catch (TransformerConfigurationException e) { 714 throw new Error(e); // impossible 715 } 716 } 717 718 /** 719 * Creates a new identity transformer. 720 */ 721 public static TransformerHandler createTransformerHandler(boolean disableSecureProcessing) { 722 try { 723 SAXTransformerFactory tf = (SAXTransformerFactory)XmlFactory.createTransformerFactory(disableSecureProcessing); 724 return tf.newTransformerHandler(); 725 } catch (TransformerConfigurationException e) { 726 throw new Error(e); // impossible 727 } 728 } 729 730 /** 731 * Creates a new DOM document. 732 */ 733 static Document createDom(boolean disableSecurityProcessing) { 734 synchronized(JAXBContextImpl.class) { 735 if(db==null) { 736 try { 737 DocumentBuilderFactory dbf = XmlFactory.createDocumentBuilderFactory(disableSecurityProcessing); 738 db = dbf.newDocumentBuilder(); 739 } catch (ParserConfigurationException e) { 740 // impossible 741 throw new FactoryConfigurationError(e); 742 } 743 } 744 return db.newDocument(); 745 } 746 } 747 748 public MarshallerImpl createMarshaller() { 749 return new MarshallerImpl(this,null); 750 } 751 752 public UnmarshallerImpl createUnmarshaller() { 753 return new UnmarshallerImpl(this,null); 754 } 755 756 public Validator createValidator() { 757 throw new UnsupportedOperationException(Messages.NOT_IMPLEMENTED_IN_2_0.format()); 758 } 759 760 @Override 761 public JAXBIntrospector createJAXBIntrospector() { 762 return new JAXBIntrospector() { 763 public boolean isElement(Object object) { 764 return getElementName(object)!=null; 765 } 766 767 public QName getElementName(Object jaxbElement) { 768 try { 769 return JAXBContextImpl.this.getElementName(jaxbElement); 770 } catch (JAXBException e) { 771 return null; 772 } 773 } 774 }; 775 } 776 777 private NonElement<Type,Class> getXmlType(RuntimeTypeInfoSet tis, TypeReference tr) { 778 if(tr==null) 779 throw new IllegalArgumentException(); 780 781 XmlJavaTypeAdapter xjta = tr.get(XmlJavaTypeAdapter.class); 782 XmlList xl = tr.get(XmlList.class); 783 784 Ref<Type,Class> ref = new Ref<Type,Class>(annotationReader, tis.getNavigator(), tr.type, xjta, xl ); 785 786 return tis.getTypeInfo(ref); 787 } 788 789 @Override 790 public void generateEpisode(Result output) { 791 if(output==null) 792 throw new IllegalArgumentException(); 793 createSchemaGenerator().writeEpisodeFile(ResultFactory.createSerializer(output)); 794 } 795 796 @Override 797 @SuppressWarnings("ThrowableInitCause") 798 public void generateSchema(SchemaOutputResolver outputResolver) throws IOException { 799 if(outputResolver==null) 800 throw new IOException(Messages.NULL_OUTPUT_RESOLVER.format()); 801 802 final SAXParseException[] e = new SAXParseException[1]; 803 final SAXParseException[] w = new SAXParseException[1]; 804 805 createSchemaGenerator().write(outputResolver, new ErrorListener() { 806 public void error(SAXParseException exception) { 807 e[0] = exception; 808 } 809 810 public void fatalError(SAXParseException exception) { 811 e[0] = exception; 812 } 813 814 public void warning(SAXParseException exception) { 815 w[0] = exception; 816 } 817 818 public void info(SAXParseException exception) {} 819 }); 820 821 if (e[0]!=null) { 822 IOException x = new IOException(Messages.FAILED_TO_GENERATE_SCHEMA.format()); 823 x.initCause(e[0]); 824 throw x; 825 } 826 if (w[0]!=null) { 827 IOException x = new IOException(Messages.ERROR_PROCESSING_SCHEMA.format()); 828 x.initCause(w[0]); 829 throw x; 830 } 831 } 832 833 private XmlSchemaGenerator<Type,Class,Field,Method> createSchemaGenerator() { 834 RuntimeTypeInfoSet tis; 835 try { 836 tis = getTypeInfoSet(); 837 } catch (IllegalAnnotationsException e) { 838 // this shouldn't happen because we've already 839 throw new AssertionError(e); 840 } 841 842 XmlSchemaGenerator<Type,Class,Field,Method> xsdgen = 843 new XmlSchemaGenerator<Type,Class,Field,Method>(tis.getNavigator(),tis); 844 845 // JAX-RPC uses Bridge objects that collide with 846 // @XmlRootElement. 847 // we will avoid collision here 848 Set<QName> rootTagNames = new HashSet<QName>(); 849 for (RuntimeElementInfo ei : tis.getAllElements()) { 850 rootTagNames.add(ei.getElementName()); 851 } 852 for (RuntimeClassInfo ci : tis.beans().values()) { 853 if(ci.isElement()) 854 rootTagNames.add(ci.asElement().getElementName()); 855 } 856 857 for (TypeReference tr : bridges.keySet()) { 858 if(rootTagNames.contains(tr.tagName)) 859 continue; 860 861 if(tr.type==void.class || tr.type==Void.class) { 862 xsdgen.add(tr.tagName,false,null); 863 } else 864 if(tr.type==CompositeStructure.class) { 865 // this is a special class we introduced for JAX-WS that we *don't* want in the schema 866 } else { 867 NonElement<Type,Class> typeInfo = getXmlType(tis,tr); 868 xsdgen.add(tr.tagName, !tis.getNavigator().isPrimitive(tr.type),typeInfo); 869 } 870 } 871 return xsdgen; 872 } 873 874 public QName getTypeName(TypeReference tr) { 875 try { 876 NonElement<Type,Class> xt = getXmlType(getTypeInfoSet(),tr); 877 if(xt==null) throw new IllegalArgumentException(); 878 return xt.getTypeName(); 879 } catch (IllegalAnnotationsException e) { 880 // impossible given that JAXBRIContext has been successfully built in the first place 881 throw new AssertionError(e); 882 } 883 } 884 885 @Override 886 public <T> Binder<T> createBinder(Class<T> domType) { 887 if(domType==Node.class) 888 return (Binder<T>)createBinder(); 889 else 890 return super.createBinder(domType); 891 } 892 893 @Override 894 public Binder<Node> createBinder() { 895 return new BinderImpl<Node>(this,new DOMScanner()); 896 } 897 898 public QName getElementName(Object o) throws JAXBException { 899 JaxBeanInfo bi = getBeanInfo(o,true); 900 if(!bi.isElement()) 901 return null; 902 return new QName(bi.getElementNamespaceURI(o),bi.getElementLocalName(o)); 903 } 904 905 public QName getElementName(Class o) throws JAXBException { 906 JaxBeanInfo bi = getBeanInfo(o,true); 907 if(!bi.isElement()) 908 return null; 909 return new QName(bi.getElementNamespaceURI(o),bi.getElementLocalName(o)); 910 } 911 912 public Bridge createBridge(TypeReference ref) { 913 return bridges.get(ref); 914 } 915 916 public @NotNull BridgeContext createBridgeContext() { 917 return new BridgeContextImpl(this); 918 } 919 920 public RawAccessor getElementPropertyAccessor(Class wrapperBean, String nsUri, String localName) throws JAXBException { 921 JaxBeanInfo bi = getBeanInfo(wrapperBean,true); 922 if(!(bi instanceof ClassBeanInfoImpl)) 923 throw new JAXBException(wrapperBean+" is not a bean"); 924 925 for( ClassBeanInfoImpl cb = (ClassBeanInfoImpl) bi; cb!=null; cb=cb.superClazz) { 926 for (Property p : cb.properties) { 927 final Accessor acc = p.getElementPropertyAccessor(nsUri,localName); 928 if(acc!=null) 929 return new RawAccessor() { 930 // Accessor.set/get are designed for unmarshaller/marshaller, and hence 931 // they go through an adapter behind the scene. 932 // this isn't desirable for JAX-WS, which essentially uses this method 933 // just as a reflection library. So use the "unadapted" version to 934 // achieve the desired semantics 935 public Object get(Object bean) throws AccessorException { 936 return acc.getUnadapted(bean); 937 } 938 939 public void set(Object bean, Object value) throws AccessorException { 940 acc.setUnadapted(bean,value); 941 } 942 }; 943 } 944 } 945 throw new JAXBException(new QName(nsUri,localName)+" is not a valid property on "+wrapperBean); 946 } 947 948 public List<String> getKnownNamespaceURIs() { 949 return Arrays.asList(nameList.namespaceURIs); 950 } 951 952 public String getBuildId() { 953 Package pkg = getClass().getPackage(); 954 if(pkg==null) return null; 955 return pkg.getImplementationVersion(); 956 } 957 958 @Override 959 public String toString() { 960 StringBuilder buf = new StringBuilder(Which.which(getClass()) + " Build-Id: " + getBuildId()); 961 buf.append("\nClasses known to this context:\n"); 962 963 Set<String> names = new TreeSet<String>(); // sort them so that it's easy to read 964 965 for (Class key : beanInfoMap.keySet()) 966 names.add(key.getName()); 967 968 for(String name: names) 969 buf.append(" ").append(name).append('\n'); 970 971 return buf.toString(); 972 } 973 974 /** 975 * Gets the value of the xmime:contentType attribute on the given object, or null 976 * if for some reason it couldn't be found, including any error. 977 */ 978 public String getXMIMEContentType( Object o ) { 979 JaxBeanInfo bi = getBeanInfo(o); 980 if(!(bi instanceof ClassBeanInfoImpl)) 981 return null; 982 983 ClassBeanInfoImpl cb = (ClassBeanInfoImpl) bi; 984 for (Property p : cb.properties) { 985 if (p instanceof AttributeProperty) { 986 AttributeProperty ap = (AttributeProperty) p; 987 if(ap.attName.equals(WellKnownNamespace.XML_MIME_URI,"contentType")) 988 try { 989 return (String)ap.xacc.print(o); 990 } catch (AccessorException e) { 991 return null; 992 } catch (SAXException e) { 993 return null; 994 } catch (ClassCastException e) { 995 return null; 996 } 997 } 998 } 999 return null; 1000 } 1001 1002 /** 1003 * Creates a {@link JAXBContextImpl} that includes the specified additional classes. 1004 */ 1005 public JAXBContextImpl createAugmented(Class<?> clazz) throws JAXBException { 1006 Class[] newList = new Class[classes.length+1]; 1007 System.arraycopy(classes,0,newList,0,classes.length); 1008 newList[classes.length] = clazz; 1009 1010 JAXBContextBuilder builder = new JAXBContextBuilder(this); 1011 builder.setClasses(newList); 1012 return builder.build(); 1013 } 1014 1015 private static final Comparator<QName> QNAME_COMPARATOR = new Comparator<QName>() { 1016 public int compare(QName lhs, QName rhs) { 1017 int r = lhs.getLocalPart().compareTo(rhs.getLocalPart()); 1018 if(r!=0) return r; 1019 1020 return lhs.getNamespaceURI().compareTo(rhs.getNamespaceURI()); 1021 } 1022 }; 1023 1024 public static class JAXBContextBuilder { 1025 1026 private boolean retainPropertyInfo = false; 1027 private boolean supressAccessorWarnings = false; 1028 private String defaultNsUri = ""; 1029 private @NotNull RuntimeAnnotationReader annotationReader = new RuntimeInlineAnnotationReader(); 1030 private @NotNull Map<Class,Class> subclassReplacements = Collections.emptyMap(); 1031 private boolean c14nSupport = false; 1032 private Class[] classes; 1033 private Collection<TypeReference> typeRefs; 1034 private boolean xmlAccessorFactorySupport = false; 1035 private boolean allNillable; 1036 private boolean improvedXsiTypeHandling = true; 1037 private boolean disableSecurityProcessing = true; 1038 private Boolean backupWithParentNamespace = null; // null for System property to be used 1039 1040 public JAXBContextBuilder() {}; 1041 1042 public JAXBContextBuilder(JAXBContextImpl baseImpl) { 1043 this.supressAccessorWarnings = baseImpl.supressAccessorWarnings; 1044 this.retainPropertyInfo = baseImpl.retainPropertyInfo; 1045 this.defaultNsUri = baseImpl.defaultNsUri; 1046 this.annotationReader = baseImpl.annotationReader; 1047 this.subclassReplacements = baseImpl.subclassReplacements; 1048 this.c14nSupport = baseImpl.c14nSupport; 1049 this.classes = baseImpl.classes; 1050 this.typeRefs = baseImpl.bridges.keySet(); 1051 this.xmlAccessorFactorySupport = baseImpl.xmlAccessorFactorySupport; 1052 this.allNillable = baseImpl.allNillable; 1053 this.disableSecurityProcessing = baseImpl.disableSecurityProcessing; 1054 this.backupWithParentNamespace = baseImpl.backupWithParentNamespace; 1055 } 1056 1057 public JAXBContextBuilder setRetainPropertyInfo(boolean val) { 1058 this.retainPropertyInfo = val; 1059 return this; 1060 } 1061 1062 public JAXBContextBuilder setSupressAccessorWarnings(boolean val) { 1063 this.supressAccessorWarnings = val; 1064 return this; 1065 } 1066 1067 public JAXBContextBuilder setC14NSupport(boolean val) { 1068 this.c14nSupport = val; 1069 return this; 1070 } 1071 1072 public JAXBContextBuilder setXmlAccessorFactorySupport(boolean val) { 1073 this.xmlAccessorFactorySupport = val; 1074 return this; 1075 } 1076 1077 public JAXBContextBuilder setDefaultNsUri(String val) { 1078 this.defaultNsUri = val; 1079 return this; 1080 } 1081 1082 public JAXBContextBuilder setAllNillable(boolean val) { 1083 this.allNillable = val; 1084 return this; 1085 } 1086 1087 public JAXBContextBuilder setClasses(Class[] val) { 1088 this.classes = val; 1089 return this; 1090 } 1091 1092 public JAXBContextBuilder setAnnotationReader(RuntimeAnnotationReader val) { 1093 this.annotationReader = val; 1094 return this; 1095 } 1096 1097 public JAXBContextBuilder setSubclassReplacements(Map<Class,Class> val) { 1098 this.subclassReplacements = val; 1099 return this; 1100 } 1101 1102 public JAXBContextBuilder setTypeRefs(Collection<TypeReference> val) { 1103 this.typeRefs = val; 1104 return this; 1105 } 1106 1107 public JAXBContextBuilder setImprovedXsiTypeHandling(boolean val) { 1108 this.improvedXsiTypeHandling = val; 1109 return this; 1110 } 1111 1112 public JAXBContextBuilder setDisableSecurityProcessing(boolean val) { 1113 this.disableSecurityProcessing = val; 1114 return this; 1115 } 1116 1117 public JAXBContextBuilder setBackupWithParentNamespace(Boolean backupWithParentNamespace) { 1118 this.backupWithParentNamespace = backupWithParentNamespace; 1119 return this; 1120 } 1121 1122 public JAXBContextImpl build() throws JAXBException { 1123 1124 // fool-proof 1125 if (this.defaultNsUri == null) { 1126 this.defaultNsUri = ""; 1127 } 1128 1129 if (this.subclassReplacements == null) { 1130 this.subclassReplacements = Collections.emptyMap(); 1131 } 1132 1133 if (this.annotationReader == null) { 1134 this.annotationReader = new RuntimeInlineAnnotationReader(); 1135 } 1136 1137 if (this.typeRefs == null) { 1138 this.typeRefs = Collections.<TypeReference>emptyList(); 1139 } 1140 1141 return new JAXBContextImpl(this); 1142 } 1143 1144 } 1145 1146 }