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