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