1 /* 2 * Copyright (c) 1997, 2012, 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.SAXResult; 69 import javax.xml.transform.sax.SAXTransformerFactory; 70 import javax.xml.transform.sax.TransformerHandler; 71 72 import com.sun.istack.internal.NotNull; 73 import com.sun.istack.internal.Pool; 74 import com.sun.xml.internal.bind.v2.WellKnownNamespace; 75 import com.sun.xml.internal.bind.api.AccessorException; 76 import com.sun.xml.internal.bind.api.Bridge; 77 import com.sun.xml.internal.bind.api.BridgeContext; 78 import com.sun.xml.internal.bind.api.CompositeStructure; 79 import com.sun.xml.internal.bind.api.ErrorListener; 80 import com.sun.xml.internal.bind.api.JAXBRIContext; 81 import com.sun.xml.internal.bind.api.RawAccessor; 82 import com.sun.xml.internal.bind.api.TypeReference; 83 import com.sun.xml.internal.bind.unmarshaller.DOMScanner; 84 import com.sun.xml.internal.bind.util.Which; 85 import com.sun.xml.internal.bind.v2.model.annotation.RuntimeAnnotationReader; 86 import com.sun.xml.internal.bind.v2.model.annotation.RuntimeInlineAnnotationReader; 87 import com.sun.xml.internal.bind.v2.model.core.Adapter; 88 import com.sun.xml.internal.bind.v2.model.core.NonElement; 89 import com.sun.xml.internal.bind.v2.model.core.Ref; 90 import com.sun.xml.internal.bind.v2.model.impl.RuntimeBuiltinLeafInfoImpl; 91 import com.sun.xml.internal.bind.v2.model.impl.RuntimeModelBuilder; 92 import com.sun.xml.internal.bind.v2.model.nav.Navigator; 93 import com.sun.xml.internal.bind.v2.model.nav.ReflectionNavigator; 94 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeArrayInfo; 95 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeBuiltinLeafInfo; 96 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeClassInfo; 97 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeElementInfo; 98 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeEnumLeafInfo; 99 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeLeafInfo; 100 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeTypeInfo; 101 import com.sun.xml.internal.bind.v2.model.runtime.RuntimeTypeInfoSet; 102 import com.sun.xml.internal.bind.v2.runtime.output.Encoded; 103 import com.sun.xml.internal.bind.v2.runtime.property.AttributeProperty; 104 import com.sun.xml.internal.bind.v2.runtime.property.Property; 105 import com.sun.xml.internal.bind.v2.runtime.reflect.Accessor; 106 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Loader; 107 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.TagName; 108 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallerImpl; 109 import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext; 110 import com.sun.xml.internal.bind.v2.schemagen.XmlSchemaGenerator; 111 import com.sun.xml.internal.bind.v2.util.EditDistance; 112 import com.sun.xml.internal.bind.v2.util.QNameMap; 113 import com.sun.xml.internal.bind.v2.util.XmlFactory; 114 import com.sun.xml.internal.txw2.output.ResultFactory; 115 116 import org.w3c.dom.Document; 117 import org.w3c.dom.Element; 118 import org.w3c.dom.Node; 119 import org.xml.sax.SAXException; 120 import org.xml.sax.SAXParseException; 121 import org.xml.sax.helpers.DefaultHandler; 122 123 /** 124 * This class provides the implementation of JAXBContext. 125 * 126 */ 127 public final class JAXBContextImpl extends JAXBRIContext { 128 129 /** 130 * All the bridge classes. 131 */ 132 private final Map<TypeReference,Bridge> bridges = new LinkedHashMap<TypeReference,Bridge>(); 133 134 /** 135 * Shared instance of {@link TransformerFactory}. 136 * Lock before use, because a {@link TransformerFactory} is not thread-safe 137 * whereas {@link JAXBContextImpl} is. 138 * Lazily created. 139 */ 140 private volatile static SAXTransformerFactory tf; 141 142 /** 143 * Shared instance of {@link DocumentBuilder}. 144 * Lock before use. Lazily created. 145 */ 146 private static DocumentBuilder db; 147 148 private final QNameMap<JaxBeanInfo> rootMap = new QNameMap<JaxBeanInfo>(); 149 private final HashMap<QName,JaxBeanInfo> typeMap = new HashMap<QName,JaxBeanInfo>(); 150 151 /** 152 * Map from JAXB-bound {@link Class} to its {@link JaxBeanInfo}. 153 */ 154 private final Map<Class,JaxBeanInfo> beanInfoMap = new LinkedHashMap<Class,JaxBeanInfo>(); 155 156 /** 157 * All created {@link JaxBeanInfo}s. 158 * Updated from each {@link JaxBeanInfo}s constructors to avoid infinite recursion 159 * for a cyclic reference. 160 * 161 * <p> 162 * This map is only used while the {@link JAXBContextImpl} is built and set to null 163 * to avoid keeping references too long. 164 */ 165 protected Map<RuntimeTypeInfo,JaxBeanInfo> beanInfos = new LinkedHashMap<RuntimeTypeInfo, JaxBeanInfo>(); 166 167 private final Map<Class/*scope*/,Map<QName,ElementBeanInfoImpl>> elements = new LinkedHashMap<Class, Map<QName, ElementBeanInfoImpl>>(); 168 169 /** 170 * Pool of {@link Marshaller}s. 171 */ 172 public final Pool<Marshaller> marshallerPool = new Pool.Impl<Marshaller>() { 173 protected @NotNull Marshaller create() { 174 return createMarshaller(); 175 } 176 }; 177 178 public final Pool<Unmarshaller> unmarshallerPool = new Pool.Impl<Unmarshaller>() { 179 protected @NotNull Unmarshaller create() { 180 return createUnmarshaller(); 181 } 182 }; 183 184 /** 185 * Used to assign indices to known names in this grammar. 186 * Reset to null once the build phase is completed. 187 */ 188 public NameBuilder nameBuilder = new NameBuilder(); 189 190 /** 191 * Keeps the list of known names. 192 * This field is set once the build pahse is completed. 193 */ 194 public final NameList nameList; 195 196 /** 197 * Input to the JAXBContext.newInstance, so that we can recreate 198 * {@link RuntimeTypeInfoSet} whenever we need. 199 */ 200 private final String defaultNsUri; 201 private final Class[] classes; 202 203 /** 204 * true to reorder attributes lexicographically in preparation of the c14n support. 205 */ 206 protected final boolean c14nSupport; 207 208 /** 209 * Flag that user has provided a custom AccessorFactory for JAXB to use 210 */ 211 public final boolean xmlAccessorFactorySupport; 212 213 /** 214 * @see JAXBRIContext#TREAT_EVERYTHING_NILLABLE 215 */ 216 public final boolean allNillable; 217 218 /** 219 * Store properties, so that they can be recovered in the run (is here because of JSON encoding of Jersey). 220 */ 221 public final boolean retainPropertyInfo; 222 223 /** 224 * Suppress reflection accessor warnings. 225 */ 226 public final boolean supressAccessorWarnings; 227 228 /** 229 * Improved xsi type handling. 230 */ 231 public final boolean improvedXsiTypeHandling; 232 233 /** 234 * Disable security processing. 235 */ 236 public final boolean disableSecurityProcessing; 237 238 private WeakReference<RuntimeTypeInfoSet> typeInfoSetCache; 239 240 private @NotNull RuntimeAnnotationReader annotationReader; 241 242 private /*almost final*/ boolean hasSwaRef; 243 private final @NotNull Map<Class,Class> subclassReplacements; 244 245 /** 246 * If true, we aim for faster {@link JAXBContext} instantiation performance, 247 * instead of going after efficient sustained unmarshalling/marshalling performance. 248 * 249 * @since 2.0.4 250 */ 251 public final boolean fastBoot; 252 253 private Set<XmlNs> xmlNsSet = null; 254 255 /** 256 * Returns declared XmlNs annotations (from package-level annotation XmlSchema 257 * 258 * @return set of all present XmlNs annotations 259 */ 260 public Set<XmlNs> getXmlNsSet() { 261 return xmlNsSet; 262 } 263 264 private JAXBContextImpl(JAXBContextBuilder builder) throws JAXBException { 265 266 this.defaultNsUri = builder.defaultNsUri; 267 this.retainPropertyInfo = builder.retainPropertyInfo; 268 this.annotationReader = builder.annotationReader; 269 this.subclassReplacements = builder.subclassReplacements; 270 this.c14nSupport = builder.c14nSupport; 271 this.classes = builder.classes; 272 this.xmlAccessorFactorySupport = builder.xmlAccessorFactorySupport; 273 this.allNillable = builder.allNillable; 274 this.supressAccessorWarnings = builder.supressAccessorWarnings; 275 this.improvedXsiTypeHandling = builder.improvedXsiTypeHandling; 276 this.disableSecurityProcessing = builder.disableSecurityProcessing; 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 ReflectionNavigator 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 = 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 = 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 <tt>c</tt> isn't a JAXB-bound class and <tt>fatal==false</tt>. 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 <tt>c</tt> isn't a JAXB-bound class and <tt>fatal==false</tt>. 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 if (tf==null) { 712 synchronized(JAXBContextImpl.class) { 713 if (tf==null) { 714 tf = (SAXTransformerFactory)XmlFactory.createTransformerFactory(disableSecureProcessing); 715 } 716 } 717 } 718 return tf.newTransformer(); 719 } catch (TransformerConfigurationException e) { 720 throw new Error(e); // impossible 721 } 722 } 723 724 /** 725 * Creates a new identity transformer. 726 */ 727 public static TransformerHandler createTransformerHandler(boolean disableSecureProcessing) { 728 try { 729 if (tf==null) { 730 synchronized(JAXBContextImpl.class) { 731 if (tf==null) { 732 tf = (SAXTransformerFactory)XmlFactory.createTransformerFactory(disableSecureProcessing); 733 } 734 } 735 } 736 return tf.newTransformerHandler(); 737 } catch (TransformerConfigurationException e) { 738 throw new Error(e); // impossible 739 } 740 } 741 742 /** 743 * Creates a new DOM document. 744 */ 745 static Document createDom(boolean disableSecurityProcessing) { 746 synchronized(JAXBContextImpl.class) { 747 if(db==null) { 748 try { 749 DocumentBuilderFactory dbf = XmlFactory.createDocumentBuilderFactory(disableSecurityProcessing); 750 db = dbf.newDocumentBuilder(); 751 } catch (ParserConfigurationException e) { 752 // impossible 753 throw new FactoryConfigurationError(e); 754 } 755 } 756 return db.newDocument(); 757 } 758 } 759 760 public MarshallerImpl createMarshaller() { 761 return new MarshallerImpl(this,null); 762 } 763 764 public UnmarshallerImpl createUnmarshaller() { 765 return new UnmarshallerImpl(this,null); 766 } 767 768 public Validator createValidator() { 769 throw new UnsupportedOperationException(Messages.NOT_IMPLEMENTED_IN_2_0.format()); 770 } 771 772 @Override 773 public JAXBIntrospector createJAXBIntrospector() { 774 return new JAXBIntrospector() { 775 public boolean isElement(Object object) { 776 return getElementName(object)!=null; 777 } 778 779 public QName getElementName(Object jaxbElement) { 780 try { 781 return JAXBContextImpl.this.getElementName(jaxbElement); 782 } catch (JAXBException e) { 783 return null; 784 } 785 } 786 }; 787 } 788 789 private NonElement<Type,Class> getXmlType(RuntimeTypeInfoSet tis, TypeReference tr) { 790 if(tr==null) 791 throw new IllegalArgumentException(); 792 793 XmlJavaTypeAdapter xjta = tr.get(XmlJavaTypeAdapter.class); 794 XmlList xl = tr.get(XmlList.class); 795 796 Ref<Type,Class> ref = new Ref<Type,Class>(annotationReader, tis.getNavigator(), tr.type, xjta, xl ); 797 798 return tis.getTypeInfo(ref); 799 } 800 801 @Override 802 public void generateEpisode(Result output) { 803 if(output==null) 804 throw new IllegalArgumentException(); 805 createSchemaGenerator().writeEpisodeFile(ResultFactory.createSerializer(output)); 806 } 807 808 @Override 809 @SuppressWarnings("ThrowableInitCause") 810 public void generateSchema(SchemaOutputResolver outputResolver) throws IOException { 811 if(outputResolver==null) 812 throw new IOException(Messages.NULL_OUTPUT_RESOLVER.format()); 813 814 final SAXParseException[] e = new SAXParseException[1]; 815 final SAXParseException[] w = new SAXParseException[1]; 816 817 createSchemaGenerator().write(outputResolver, new ErrorListener() { 818 public void error(SAXParseException exception) { 819 e[0] = exception; 820 } 821 822 public void fatalError(SAXParseException exception) { 823 e[0] = exception; 824 } 825 826 public void warning(SAXParseException exception) { 827 w[0] = exception; 828 } 829 830 public void info(SAXParseException exception) {} 831 }); 832 833 if (e[0]!=null) { 834 IOException x = new IOException(Messages.FAILED_TO_GENERATE_SCHEMA.format()); 835 x.initCause(e[0]); 836 throw x; 837 } 838 if (w[0]!=null) { 839 IOException x = new IOException(Messages.ERROR_PROCESSING_SCHEMA.format()); 840 x.initCause(w[0]); 841 throw x; 842 } 843 } 844 845 private XmlSchemaGenerator<Type,Class,Field,Method> createSchemaGenerator() { 846 RuntimeTypeInfoSet tis; 847 try { 848 tis = getTypeInfoSet(); 849 } catch (IllegalAnnotationsException e) { 850 // this shouldn't happen because we've already 851 throw new AssertionError(e); 852 } 853 854 XmlSchemaGenerator<Type,Class,Field,Method> xsdgen = 855 new XmlSchemaGenerator<Type,Class,Field,Method>(tis.getNavigator(),tis); 856 857 // JAX-RPC uses Bridge objects that collide with 858 // @XmlRootElement. 859 // we will avoid collision here 860 Set<QName> rootTagNames = new HashSet<QName>(); 861 for (RuntimeElementInfo ei : tis.getAllElements()) { 862 rootTagNames.add(ei.getElementName()); 863 } 864 for (RuntimeClassInfo ci : tis.beans().values()) { 865 if(ci.isElement()) 866 rootTagNames.add(ci.asElement().getElementName()); 867 } 868 869 for (TypeReference tr : bridges.keySet()) { 870 if(rootTagNames.contains(tr.tagName)) 871 continue; 872 873 if(tr.type==void.class || tr.type==Void.class) { 874 xsdgen.add(tr.tagName,false,null); 875 } else 876 if(tr.type==CompositeStructure.class) { 877 // this is a special class we introduced for JAX-WS that we *don't* want in the schema 878 } else { 879 NonElement<Type,Class> typeInfo = getXmlType(tis,tr); 880 xsdgen.add(tr.tagName, !Navigator.REFLECTION.isPrimitive(tr.type),typeInfo); 881 } 882 } 883 return xsdgen; 884 } 885 886 public QName getTypeName(TypeReference tr) { 887 try { 888 NonElement<Type,Class> xt = getXmlType(getTypeInfoSet(),tr); 889 if(xt==null) throw new IllegalArgumentException(); 890 return xt.getTypeName(); 891 } catch (IllegalAnnotationsException e) { 892 // impossible given that JAXBRIContext has been successfully built in the first place 893 throw new AssertionError(e); 894 } 895 } 896 897 /** 898 * Used for testing. 899 */ 900 public SchemaOutputResolver createTestResolver() { 901 return new SchemaOutputResolver() { 902 public Result createOutput(String namespaceUri, String suggestedFileName) { 903 SAXResult r = new SAXResult(new DefaultHandler()); 904 r.setSystemId(suggestedFileName); 905 return r; 906 } 907 }; 908 } 909 910 @Override 911 public <T> Binder<T> createBinder(Class<T> domType) { 912 if(domType==Node.class) 913 return (Binder<T>)createBinder(); 914 else 915 return super.createBinder(domType); 916 } 917 918 @Override 919 public Binder<Node> createBinder() { 920 return new BinderImpl<Node>(this,new DOMScanner()); 921 } 922 923 public QName getElementName(Object o) throws JAXBException { 924 JaxBeanInfo bi = getBeanInfo(o,true); 925 if(!bi.isElement()) 926 return null; 927 return new QName(bi.getElementNamespaceURI(o),bi.getElementLocalName(o)); 928 } 929 930 public QName getElementName(Class o) throws JAXBException { 931 JaxBeanInfo bi = getBeanInfo(o,true); 932 if(!bi.isElement()) 933 return null; 934 return new QName(bi.getElementNamespaceURI(o),bi.getElementLocalName(o)); 935 } 936 937 public Bridge createBridge(TypeReference ref) { 938 return bridges.get(ref); 939 } 940 941 public @NotNull BridgeContext createBridgeContext() { 942 return new BridgeContextImpl(this); 943 } 944 945 public RawAccessor getElementPropertyAccessor(Class wrapperBean, String nsUri, String localName) throws JAXBException { 946 JaxBeanInfo bi = getBeanInfo(wrapperBean,true); 947 if(!(bi instanceof ClassBeanInfoImpl)) 948 throw new JAXBException(wrapperBean+" is not a bean"); 949 950 for( ClassBeanInfoImpl cb = (ClassBeanInfoImpl) bi; cb!=null; cb=cb.superClazz) { 951 for (Property p : cb.properties) { 952 final Accessor acc = p.getElementPropertyAccessor(nsUri,localName); 953 if(acc!=null) 954 return new RawAccessor() { 955 // Accessor.set/get are designed for unmarshaller/marshaller, and hence 956 // they go through an adapter behind the scene. 957 // this isn't desirable for JAX-WS, which essentially uses this method 958 // just as a reflection library. So use the "unadapted" version to 959 // achieve the desired semantics 960 public Object get(Object bean) throws AccessorException { 961 return acc.getUnadapted(bean); 962 } 963 964 public void set(Object bean, Object value) throws AccessorException { 965 acc.setUnadapted(bean,value); 966 } 967 }; 968 } 969 } 970 throw new JAXBException(new QName(nsUri,localName)+" is not a valid property on "+wrapperBean); 971 } 972 973 public List<String> getKnownNamespaceURIs() { 974 return Arrays.asList(nameList.namespaceURIs); 975 } 976 977 public String getBuildId() { 978 Package pkg = getClass().getPackage(); 979 if(pkg==null) return null; 980 return pkg.getImplementationVersion(); 981 } 982 983 @Override 984 public String toString() { 985 StringBuilder buf = new StringBuilder(Which.which(getClass()) + " Build-Id: " + getBuildId()); 986 buf.append("\nClasses known to this context:\n"); 987 988 Set<String> names = new TreeSet<String>(); // sort them so that it's easy to read 989 990 for (Class key : beanInfoMap.keySet()) 991 names.add(key.getName()); 992 993 for(String name: names) 994 buf.append(" ").append(name).append('\n'); 995 996 return buf.toString(); 997 } 998 999 /** 1000 * Gets the value of the xmime:contentType attribute on the given object, or null 1001 * if for some reason it couldn't be found, including any error. 1002 */ 1003 public String getXMIMEContentType( Object o ) { 1004 JaxBeanInfo bi = getBeanInfo(o); 1005 if(!(bi instanceof ClassBeanInfoImpl)) 1006 return null; 1007 1008 ClassBeanInfoImpl cb = (ClassBeanInfoImpl) bi; 1009 for (Property p : cb.properties) { 1010 if (p instanceof AttributeProperty) { 1011 AttributeProperty ap = (AttributeProperty) p; 1012 if(ap.attName.equals(WellKnownNamespace.XML_MIME_URI,"contentType")) 1013 try { 1014 return (String)ap.xacc.print(o); 1015 } catch (AccessorException e) { 1016 return null; 1017 } catch (SAXException e) { 1018 return null; 1019 } catch (ClassCastException e) { 1020 return null; 1021 } 1022 } 1023 } 1024 return null; 1025 } 1026 1027 /** 1028 * Creates a {@link JAXBContextImpl} that includes the specified additional classes. 1029 */ 1030 public JAXBContextImpl createAugmented(Class<?> clazz) throws JAXBException { 1031 Class[] newList = new Class[classes.length+1]; 1032 System.arraycopy(classes,0,newList,0,classes.length); 1033 newList[classes.length] = clazz; 1034 1035 JAXBContextBuilder builder = new JAXBContextBuilder(this); 1036 builder.setClasses(newList); 1037 return builder.build(); 1038 } 1039 1040 private static final Comparator<QName> QNAME_COMPARATOR = new Comparator<QName>() { 1041 public int compare(QName lhs, QName rhs) { 1042 int r = lhs.getLocalPart().compareTo(rhs.getLocalPart()); 1043 if(r!=0) return r; 1044 1045 return lhs.getNamespaceURI().compareTo(rhs.getNamespaceURI()); 1046 } 1047 }; 1048 1049 public static class JAXBContextBuilder { 1050 1051 private boolean retainPropertyInfo = false; 1052 private boolean supressAccessorWarnings = false; 1053 private String defaultNsUri = ""; 1054 private @NotNull RuntimeAnnotationReader annotationReader = new RuntimeInlineAnnotationReader(); 1055 private @NotNull Map<Class,Class> subclassReplacements = Collections.emptyMap(); 1056 private boolean c14nSupport = false; 1057 private Class[] classes; 1058 private Collection<TypeReference> typeRefs; 1059 private boolean xmlAccessorFactorySupport = false; 1060 private boolean allNillable; 1061 private boolean improvedXsiTypeHandling = true; 1062 private boolean disableSecurityProcessing = true; 1063 1064 public JAXBContextBuilder() {}; 1065 1066 public JAXBContextBuilder(JAXBContextImpl baseImpl) { 1067 this.supressAccessorWarnings = baseImpl.supressAccessorWarnings; 1068 this.retainPropertyInfo = baseImpl.retainPropertyInfo; 1069 this.defaultNsUri = baseImpl.defaultNsUri; 1070 this.annotationReader = baseImpl.annotationReader; 1071 this.subclassReplacements = baseImpl.subclassReplacements; 1072 this.c14nSupport = baseImpl.c14nSupport; 1073 this.classes = baseImpl.classes; 1074 this.typeRefs = baseImpl.bridges.keySet(); 1075 this.xmlAccessorFactorySupport = baseImpl.xmlAccessorFactorySupport; 1076 this.allNillable = baseImpl.allNillable; 1077 this.disableSecurityProcessing = baseImpl.disableSecurityProcessing; 1078 } 1079 1080 public JAXBContextBuilder setRetainPropertyInfo(boolean val) { 1081 this.retainPropertyInfo = val; 1082 return this; 1083 } 1084 1085 public JAXBContextBuilder setSupressAccessorWarnings(boolean val) { 1086 this.supressAccessorWarnings = val; 1087 return this; 1088 } 1089 1090 public JAXBContextBuilder setC14NSupport(boolean val) { 1091 this.c14nSupport = val; 1092 return this; 1093 } 1094 1095 public JAXBContextBuilder setXmlAccessorFactorySupport(boolean val) { 1096 this.xmlAccessorFactorySupport = val; 1097 return this; 1098 } 1099 1100 public JAXBContextBuilder setDefaultNsUri(String val) { 1101 this.defaultNsUri = val; 1102 return this; 1103 } 1104 1105 public JAXBContextBuilder setAllNillable(boolean val) { 1106 this.allNillable = val; 1107 return this; 1108 } 1109 1110 public JAXBContextBuilder setClasses(Class[] val) { 1111 this.classes = val; 1112 return this; 1113 } 1114 1115 public JAXBContextBuilder setAnnotationReader(RuntimeAnnotationReader val) { 1116 this.annotationReader = val; 1117 return this; 1118 } 1119 1120 public JAXBContextBuilder setSubclassReplacements(Map<Class,Class> val) { 1121 this.subclassReplacements = val; 1122 return this; 1123 } 1124 1125 public JAXBContextBuilder setTypeRefs(Collection<TypeReference> val) { 1126 this.typeRefs = val; 1127 return this; 1128 } 1129 1130 public JAXBContextBuilder setImprovedXsiTypeHandling(boolean val) { 1131 this.improvedXsiTypeHandling = val; 1132 return this; 1133 } 1134 1135 public JAXBContextBuilder setDisableSecurityProcessing(boolean val) { 1136 this.disableSecurityProcessing = val; 1137 return this; 1138 } 1139 1140 public JAXBContextImpl build() throws JAXBException { 1141 1142 // fool-proof 1143 if (this.defaultNsUri == null) { 1144 this.defaultNsUri = ""; 1145 } 1146 1147 if (this.subclassReplacements == null) { 1148 this.subclassReplacements = Collections.emptyMap(); 1149 } 1150 1151 if (this.annotationReader == null) { 1152 this.annotationReader = new RuntimeInlineAnnotationReader(); 1153 } 1154 1155 if (this.typeRefs == null) { 1156 this.typeRefs = Collections.<TypeReference>emptyList(); 1157 } 1158 1159 return new JAXBContextImpl(this); 1160 } 1161 1162 } 1163 1164 }