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.unmarshaller; 27 28 import java.lang.reflect.InvocationTargetException; 29 import java.lang.reflect.Method; 30 import java.util.ArrayList; 31 import java.util.Collection; 32 import java.util.Collections; 33 import java.util.HashMap; 34 import java.util.Iterator; 35 import java.util.List; 36 import java.util.Map; 37 import java.util.concurrent.Callable; 38 import java.util.logging.Level; 39 import java.util.logging.Logger; 40 41 import javax.xml.XMLConstants; 42 import javax.xml.bind.JAXBElement; 43 import javax.xml.bind.UnmarshalException; 44 import javax.xml.bind.Unmarshaller; 45 import javax.xml.bind.ValidationEvent; 46 import javax.xml.bind.ValidationEventHandler; 47 import javax.xml.bind.ValidationEventLocator; 48 import javax.xml.bind.helpers.ValidationEventImpl; 49 import javax.xml.namespace.NamespaceContext; 50 import javax.xml.namespace.QName; 51 52 import com.sun.istack.internal.NotNull; 53 import com.sun.istack.internal.Nullable; 54 import com.sun.istack.internal.SAXParseException2; 55 import com.sun.xml.internal.bind.IDResolver; 56 import com.sun.xml.internal.bind.Util; 57 import com.sun.xml.internal.bind.api.AccessorException; 58 import com.sun.xml.internal.bind.api.ClassResolver; 59 import com.sun.xml.internal.bind.unmarshaller.InfosetScanner; 60 import com.sun.xml.internal.bind.v2.ClassFactory; 61 import com.sun.xml.internal.bind.v2.runtime.AssociationMap; 62 import com.sun.xml.internal.bind.v2.runtime.Coordinator; 63 import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl; 64 import com.sun.xml.internal.bind.v2.runtime.JaxBeanInfo; 65 import java.util.logging.Level; 66 import java.util.logging.Logger; 67 68 import org.xml.sax.ErrorHandler; 69 import org.xml.sax.SAXException; 70 import org.xml.sax.helpers.LocatorImpl; 71 72 /** 73 * Center of the unmarshalling. 74 * 75 * <p> 76 * This object is responsible for coordinating {@link Loader}s to 77 * perform the whole unmarshalling. 78 * 79 * @author Kohsuke Kawaguchi 80 */ 81 public final class UnmarshallingContext extends Coordinator 82 implements NamespaceContext, ValidationEventHandler, ErrorHandler, XmlVisitor, XmlVisitor.TextPredictor { 83 84 private static final Logger logger = Logger.getLogger(UnmarshallingContext.class.getName()); 85 86 /** 87 * Root state. 88 */ 89 private final State root; 90 91 /** 92 * The currently active state. 93 */ 94 private State current; 95 96 private static final LocatorEx DUMMY_INSTANCE; 97 98 static { 99 LocatorImpl loc = new LocatorImpl(); 100 loc.setPublicId(null); 101 loc.setSystemId(null); 102 loc.setLineNumber(-1); 103 loc.setColumnNumber(-1); 104 DUMMY_INSTANCE = new LocatorExWrapper(loc); 105 } 106 107 private @NotNull LocatorEx locator = DUMMY_INSTANCE; 108 109 /** Root object that is being unmarshalled. */ 110 private Object result; 111 112 /** 113 * If non-null, this unmarshaller will unmarshal {@code JAXBElement<EXPECTEDTYPE>} 114 * regardless of the tag name, as opposed to deciding the root object by using 115 * the tag name. 116 * 117 * The property has a package-level access, because we cannot copy this value 118 * to {@link UnmarshallingContext} when it is created. The property 119 * on {@link Unmarshaller} could be changed after the handler is created. 120 */ 121 private JaxBeanInfo expectedType; 122 123 /** 124 * Handles ID/IDREF. 125 */ 126 private IDResolver idResolver; 127 128 /** 129 * This flag is set to true at the startDocument event 130 * and false at the endDocument event. 131 * 132 * Until the first document is unmarshalled, we don't 133 * want to return an object. So this variable is initialized 134 * to true. 135 */ 136 private boolean isUnmarshalInProgress = true; 137 private boolean aborted = false; 138 139 public final UnmarshallerImpl parent; 140 141 /** 142 * If the unmarshaller is doing associative unmarshalling, 143 * this field is initialized to non-null. 144 */ 145 private final AssociationMap assoc; 146 147 /** 148 * Indicates whether we are doing in-place unmarshalling 149 * or not. 150 * 151 * <p> 152 * This flag is unused when {@link #assoc}==null. 153 * If it's non-null, then <tt>true</tt> indicates 154 * that we are doing in-place associative unmarshalling. 155 * If <tt>false</tt>, then we are doing associative unmarshalling 156 * without object reuse. 157 */ 158 private boolean isInplaceMode; 159 160 /** 161 * This object is consulted to get the element object for 162 * the current element event. 163 * 164 * This is used when we are building an association map. 165 */ 166 private InfosetScanner scanner; 167 168 private Object currentElement; 169 170 /** 171 * @see XmlVisitor#startDocument(LocatorEx, NamespaceContext) 172 */ 173 private NamespaceContext environmentNamespaceContext; 174 175 /** 176 * Used to discover additional classes when we hit unknown elements/types. 177 */ 178 public @Nullable ClassResolver classResolver; 179 180 /** 181 * User-supplied {@link ClassLoader} for converting name to {@link Class}. 182 * For backward compatibility, when null, use thread context classloader. 183 */ 184 public @Nullable ClassLoader classLoader; 185 186 /** 187 * The variable introduced to avoid reporting n^10 similar errors. 188 * After error is reported counter is decremented. When it became 0 - errors should not be reported any more. 189 * 190 * volatile is required to ensure that concurrent threads will see changed value 191 */ 192 private static volatile int errorsCounter = 10; 193 194 /** 195 * State information for each element. 196 */ 197 public final class State { 198 /** 199 * Loader that owns this element. 200 */ 201 public Loader loader; 202 /** 203 * Once {@link #loader} is completed, this receiver 204 * receives the result. 205 */ 206 public Receiver receiver; 207 208 public Intercepter intercepter; 209 210 211 /** 212 * Object being unmarshalled by this {@link #loader}. 213 */ 214 public Object target; 215 216 /** 217 * Hack for making JAXBElement unmarshalling work. 218 * 219 * <p> 220 * While the unmarshalling is in progress, the {@link #target} field stores the object being unmarshalled. 221 * This makes it convenient to keep track of the unmarshalling activity in context of XML infoset, but 222 * since there's only one {@link State} per element, this mechanism only works when there's one object 223 * per element, which breaks down when we have {@link JAXBElement}, since the presence of JAXBElement 224 * requires that we have two objects unmarshalled (a JAXBElement X and a value object Y bound to an XML type.) 225 * 226 * <p> 227 * So to make room for storing both, this {@link #backup} field is used. When we create X instance 228 * in the above example, we set that to {@code state.prev.target} and displace its old value to 229 * {@code state.prev.backup} (where Y goes to {@code state.target}.) Upon the completion of the unmarshalling 230 * of Y, we revert this. 231 * 232 * <p> 233 * While this attributes X incorrectly to its parent element, this preserves the parent/child 234 * relationship between unmarshalled objects and {@link State} parent/child relationship, and 235 * it thereby makes {@link Receiver} mechanism simpler. 236 * 237 * <p> 238 * Yes, I know this is a hack, and no, I'm not proud of it. 239 * 240 * @see ElementBeanInfoImpl.IntercepterLoader#startElement(State, TagName) 241 * @see ElementBeanInfoImpl.IntercepterLoader#intercept(State, Object) 242 */ 243 public Object backup; 244 245 /** 246 * Number of {@link UnmarshallingContext#nsBind}s declared thus far. 247 * (The value of {@link UnmarshallingContext#nsLen} when this state is pushed. 248 */ 249 private int numNsDecl; 250 251 /** 252 * If this element has an element default value. 253 * 254 * This should be set by either a parent {@link Loader} when 255 * {@link Loader#childElement(State, TagName)} is called 256 * or by a child {@link Loader} when 257 * {@link Loader#startElement(State, TagName)} is called. 258 */ 259 public String elementDefaultValue; 260 261 /** 262 * {@link State} for the parent element 263 * 264 * {@link State} objects form a doubly linked list. 265 */ 266 public State prev; 267 private State next; 268 269 public boolean nil = false; 270 271 /** 272 * Gets the context. 273 */ 274 public UnmarshallingContext getContext() { 275 return UnmarshallingContext.this; 276 } 277 278 @SuppressWarnings("LeakingThisInConstructor") 279 private State(State prev) { 280 this.prev = prev; 281 if (prev!=null) { 282 prev.next = this; 283 } 284 } 285 286 private void push() { 287 if (logger.isLoggable(Level.FINEST)) { 288 logger.log(Level.FINEST, "State.push"); 289 } 290 if (next==null) { 291 assert current == this; 292 allocateMoreStates(); 293 } 294 nil = false; 295 State n = next; 296 n.numNsDecl = nsLen; 297 current = n; 298 } 299 300 private void pop() { 301 if (logger.isLoggable(Level.FINEST)) { 302 logger.log(Level.FINEST, "State.pop"); 303 } 304 assert prev!=null; 305 loader = null; 306 nil = false; 307 receiver = null; 308 intercepter = null; 309 elementDefaultValue = null; 310 target = null; 311 current = prev; 312 } 313 } 314 315 /** 316 * Stub to the user-specified factory method. 317 */ 318 private static class Factory { 319 private final Object factorInstance; 320 private final Method method; 321 322 public Factory(Object factorInstance, Method method) { 323 this.factorInstance = factorInstance; 324 this.method = method; 325 } 326 327 public Object createInstance() throws SAXException { 328 try { 329 return method.invoke(factorInstance); 330 } catch (IllegalAccessException e) { 331 getInstance().handleError(e,false); 332 } catch (InvocationTargetException e) { 333 getInstance().handleError(e,false); 334 } 335 return null; // can never be executed 336 } 337 } 338 339 340 /** 341 * Creates a new unmarshaller. 342 * 343 * @param assoc 344 * Must be both non-null when the unmarshaller does the 345 * in-place unmarshalling. Otherwise must be both null. 346 */ 347 public UnmarshallingContext( UnmarshallerImpl _parent, AssociationMap assoc) { 348 this.parent = _parent; 349 this.assoc = assoc; 350 this.root = this.current = new State(null); 351 allocateMoreStates(); 352 } 353 354 public void reset(InfosetScanner scanner,boolean isInplaceMode, JaxBeanInfo expectedType, IDResolver idResolver) { 355 this.scanner = scanner; 356 this.isInplaceMode = isInplaceMode; 357 this.expectedType = expectedType; 358 this.idResolver = idResolver; 359 } 360 361 public JAXBContextImpl getJAXBContext() { 362 return parent.context; 363 } 364 365 public State getCurrentState() { 366 return current; 367 } 368 369 /** 370 * On top of {@link JAXBContextImpl#selectRootLoader(State, TagName)}, 371 * this method also consults {@link ClassResolver}. 372 * 373 * @throws SAXException 374 * if {@link ValidationEventHandler} reported a failure. 375 */ 376 public Loader selectRootLoader(State state, TagName tag) throws SAXException { 377 try { 378 Loader l = getJAXBContext().selectRootLoader(state, tag); 379 if(l!=null) return l; 380 381 if(classResolver!=null) { 382 Class<?> clazz = classResolver.resolveElementName(tag.uri, tag.local); 383 if(clazz!=null) { 384 JAXBContextImpl enhanced = getJAXBContext().createAugmented(clazz); 385 JaxBeanInfo<?> bi = enhanced.getBeanInfo(clazz); 386 return bi.getLoader(enhanced,true); 387 } 388 } 389 } catch (RuntimeException e) { 390 throw e; 391 } catch (Exception e) { 392 handleError(e); 393 } 394 395 return null; 396 } 397 398 /** 399 * Allocates a few more {@link State}s. 400 * 401 * Allocating multiple {@link State}s at once allows those objects 402 * to be allocated near each other, which reduces the working set 403 * of CPU. It improves the chance the relevant data is in the cache. 404 */ 405 private void allocateMoreStates() { 406 // this method should be used only when we run out of a state. 407 assert current.next==null; 408 409 State s = current; 410 for (int i=0; i<8; i++) { 411 s = new State(s); 412 } 413 } 414 415 public void clearStates() { 416 State last = current; 417 while (last.next != null) last = last.next; 418 while (last.prev != null) { 419 last.loader = null; 420 last.nil = false; 421 last.receiver = null; 422 last.intercepter = null; 423 last.elementDefaultValue = null; 424 last.target = null; 425 last = last.prev; 426 last.next.prev = null; 427 last.next = null; 428 } 429 current = last; 430 } 431 432 /** 433 * User-specified factory methods. 434 */ 435 private final Map<Class,Factory> factories = new HashMap<Class, Factory>(); 436 437 public void setFactories(Object factoryInstances) { 438 factories.clear(); 439 if(factoryInstances==null) { 440 return; 441 } 442 if(factoryInstances instanceof Object[]) { 443 for( Object factory : (Object[])factoryInstances ) { 444 // look for all the public methods inlcuding derived ones 445 addFactory(factory); 446 } 447 } else { 448 addFactory(factoryInstances); 449 } 450 } 451 452 private void addFactory(Object factory) { 453 for( Method m : factory.getClass().getMethods() ) { 454 // look for methods whose signature is T createXXX() 455 if(!m.getName().startsWith("create")) 456 continue; 457 if(m.getParameterTypes().length>0) 458 continue; 459 460 Class type = m.getReturnType(); 461 462 factories.put(type,new Factory(factory,m)); 463 } 464 } 465 466 @Override 467 public void startDocument(LocatorEx locator, NamespaceContext nsContext) throws SAXException { 468 if(locator!=null) 469 this.locator = locator; 470 this.environmentNamespaceContext = nsContext; 471 // reset the object 472 result = null; 473 current = root; 474 475 patchersLen=0; 476 aborted = false; 477 isUnmarshalInProgress = true; 478 nsLen=0; 479 480 if(expectedType!=null) 481 root.loader = EXPECTED_TYPE_ROOT_LOADER; 482 else 483 root.loader = DEFAULT_ROOT_LOADER; 484 485 idResolver.startDocument(this); 486 } 487 488 @Override 489 public void startElement(TagName tagName) throws SAXException { 490 pushCoordinator(); 491 try { 492 _startElement(tagName); 493 } finally { 494 popCoordinator(); 495 } 496 } 497 498 private void _startElement(TagName tagName) throws SAXException { 499 // remember the current element if we are interested in it. 500 // because the inner peer might not be found while we consume 501 // the enter element token, we need to keep this information 502 // longer than this callback. That's why we assign it to a field. 503 if( assoc!=null ) 504 currentElement = scanner.getCurrentElement(); 505 506 Loader h = current.loader; 507 current.push(); 508 509 // tell the parent about the new child 510 h.childElement(current,tagName); 511 assert current.loader!=null; // the childElement should register this 512 // and tell the new child that you are activated 513 current.loader.startElement(current,tagName); 514 } 515 516 @Override 517 public void text(CharSequence pcdata) throws SAXException { 518 State cur = current; 519 pushCoordinator(); 520 try { 521 if(cur.elementDefaultValue!=null) { 522 if(pcdata.length()==0) { 523 // send the default value into the unmarshaller instead 524 pcdata = cur.elementDefaultValue; 525 } 526 } 527 cur.loader.text(cur,pcdata); 528 } finally { 529 popCoordinator(); 530 } 531 } 532 533 @Override 534 public final void endElement(TagName tagName) throws SAXException { 535 pushCoordinator(); 536 try { 537 State child = current; 538 539 // tell the child that your time is up 540 child.loader.leaveElement(child,tagName); 541 542 // child.pop will erase them so store them now 543 Object target = child.target; 544 Receiver recv = child.receiver; 545 Intercepter intercepter = child.intercepter; 546 child.pop(); 547 548 // then let the parent know 549 if(intercepter!=null) 550 target = intercepter.intercept(current,target); 551 if(recv!=null) 552 recv.receive(current,target); 553 } finally { 554 popCoordinator(); 555 } 556 } 557 558 @Override 559 public void endDocument() throws SAXException { 560 runPatchers(); 561 idResolver.endDocument(); 562 563 isUnmarshalInProgress = false; 564 currentElement = null; 565 locator = DUMMY_INSTANCE; 566 environmentNamespaceContext = null; 567 568 // at the successful completion, scope must be all closed 569 assert root==current; 570 } 571 572 /** 573 * You should be always calling this through {@link TextPredictor}. 574 */ 575 @Deprecated 576 @Override 577 public boolean expectText() { 578 return current.loader.expectText; 579 } 580 581 /** 582 * You should be always getting {@link TextPredictor} from {@link XmlVisitor}. 583 */ 584 @Deprecated 585 @Override 586 public TextPredictor getPredictor() { 587 return this; 588 } 589 590 @Override 591 public UnmarshallingContext getContext() { 592 return this; 593 } 594 595 /** 596 * Gets the result of the unmarshalling 597 */ 598 public Object getResult() throws UnmarshalException { 599 if(isUnmarshalInProgress) 600 throw new IllegalStateException(); 601 602 if(!aborted) return result; 603 604 // there was an error. 605 throw new UnmarshalException((String)null); 606 } 607 608 void clearResult() { 609 if (isUnmarshalInProgress) { 610 throw new IllegalStateException(); 611 } 612 result = null; 613 } 614 615 /** 616 * Creates a new instance of the specified class. 617 * In the unmarshaller, we need to check the user-specified factory class. 618 */ 619 public Object createInstance( Class<?> clazz ) throws SAXException { 620 if(!factories.isEmpty()) { 621 Factory factory = factories.get(clazz); 622 if(factory!=null) 623 return factory.createInstance(); 624 } 625 return ClassFactory.create(clazz); 626 } 627 628 /** 629 * Creates a new instance of the specified class. 630 * In the unmarshaller, we need to check the user-specified factory class. 631 */ 632 public Object createInstance( JaxBeanInfo beanInfo ) throws SAXException { 633 if(!factories.isEmpty()) { 634 Factory factory = factories.get(beanInfo.jaxbType); 635 if(factory!=null) 636 return factory.createInstance(); 637 } 638 try { 639 return beanInfo.createInstance(this); 640 } catch (IllegalAccessException e) { 641 Loader.reportError("Unable to create an instance of "+beanInfo.jaxbType.getName(),e,false); 642 } catch (InvocationTargetException e) { 643 Loader.reportError("Unable to create an instance of "+beanInfo.jaxbType.getName(),e,false); 644 } catch (InstantiationException e) { 645 Loader.reportError("Unable to create an instance of "+beanInfo.jaxbType.getName(),e,false); 646 } 647 return null; // can never be here 648 } 649 650 651 652 // 653 // 654 // error handling 655 // 656 // 657 658 /** 659 * Reports an error to the user, and asks if s/he wants 660 * to recover. If the canRecover flag is false, regardless 661 * of the client instruction, an exception will be thrown. 662 * 663 * Only if the flag is true and the user wants to recover from an error, 664 * the method returns normally. 665 * 666 * The thrown exception will be catched by the unmarshaller. 667 */ 668 public void handleEvent(ValidationEvent event, boolean canRecover ) throws SAXException { 669 ValidationEventHandler eventHandler = parent.getEventHandler(); 670 671 boolean recover = eventHandler.handleEvent(event); 672 673 // if the handler says "abort", we will not return the object 674 // from the unmarshaller.getResult() 675 if(!recover) aborted = true; 676 677 if( !canRecover || !recover ) 678 throw new SAXParseException2( event.getMessage(), locator, 679 new UnmarshalException( 680 event.getMessage(), 681 event.getLinkedException() ) ); 682 } 683 684 @Override 685 public boolean handleEvent(ValidationEvent event) { 686 try { 687 // if the handler says "abort", we will not return the object. 688 boolean recover = parent.getEventHandler().handleEvent(event); 689 if(!recover) aborted = true; 690 return recover; 691 } catch( RuntimeException re ) { 692 // if client event handler causes a runtime exception, then we 693 // have to return false. 694 return false; 695 } 696 } 697 698 /** 699 * Reports an exception found during the unmarshalling to the user. 700 * This method is a convenience method that calls into 701 * {@link #handleEvent(ValidationEvent, boolean)} 702 */ 703 public void handleError(Exception e) throws SAXException { 704 handleError(e,true); 705 } 706 707 public void handleError(Exception e,boolean canRecover) throws SAXException { 708 handleEvent(new ValidationEventImpl(ValidationEvent.ERROR,e.getMessage(),locator.getLocation(),e),canRecover); 709 } 710 711 public void handleError(String msg) { 712 handleEvent(new ValidationEventImpl(ValidationEvent.ERROR,msg,locator.getLocation())); 713 } 714 715 @Override 716 protected ValidationEventLocator getLocation() { 717 return locator.getLocation(); 718 } 719 720 /** 721 * Gets the current source location information in SAX {@link Locator}. 722 * <p> 723 * Sometimes the unmarshaller works against a different kind of XML source, 724 * making this information meaningless. 725 */ 726 public LocatorEx getLocator() { return locator; } 727 728 /** 729 * Called when there's no corresponding ID value. 730 */ 731 public void errorUnresolvedIDREF(Object bean, String idref, LocatorEx loc) throws SAXException { 732 handleEvent( new ValidationEventImpl( 733 ValidationEvent.ERROR, 734 Messages.UNRESOLVED_IDREF.format(idref), 735 loc.getLocation()), true ); 736 } 737 738 739 // 740 // 741 // ID/IDREF related code 742 // 743 // 744 /** 745 * Submitted patchers in the order they've submitted. 746 * Many XML vocabulary doesn't use ID/IDREF at all, so we 747 * initialize it with null. 748 */ 749 private Patcher[] patchers = null; 750 private int patchersLen = 0; 751 752 /** 753 * Adds a job that will be executed at the last of the unmarshalling. 754 * This method is used to support ID/IDREF feature, but it can be used 755 * for other purposes as well. 756 * 757 * @param job 758 * The run method of this object is called. 759 */ 760 public void addPatcher( Patcher job ) { 761 // re-allocate buffer if necessary 762 if( patchers==null ) 763 patchers = new Patcher[32]; 764 if( patchers.length == patchersLen ) { 765 Patcher[] buf = new Patcher[patchersLen*2]; 766 System.arraycopy(patchers,0,buf,0,patchersLen); 767 patchers = buf; 768 } 769 patchers[patchersLen++] = job; 770 } 771 772 /** Executes all the patchers. */ 773 private void runPatchers() throws SAXException { 774 if( patchers!=null ) { 775 for( int i=0; i<patchersLen; i++ ) { 776 patchers[i].run(); 777 patchers[i] = null; // free memory 778 } 779 } 780 } 781 782 /** 783 * Adds the object which is currently being unmarshalled 784 * to the ID table. 785 * 786 * @return 787 * Returns the value passed as the parameter. 788 * This is a hack, but this makes it easier for ID 789 * transducer to do its job. 790 */ 791 // TODO: what shall we do if the ID is already declared? 792 // 793 // throwing an exception is one way. Overwriting the previous one 794 // is another way. The latter allows us to process invalid documents, 795 // while the former makes it impossible to handle them. 796 // 797 // I prefer to be flexible in terms of invalid document handling, 798 // so chose not to throw an exception. 799 // 800 // I believe this is an implementation choice, not the spec issue. 801 // -kk 802 public String addToIdTable( String id ) throws SAXException { 803 // Hmm... 804 // in cases such as when ID is used as an attribute, or as @XmlValue 805 // the target wilil be current.target. 806 // but in some other cases, such as when ID is used as a child element 807 // or a value of JAXBElement, it's current.prev.target. 808 // I don't know if this detection logic is complete 809 Object o = current.target; 810 if(o==null) 811 o = current.prev.target; 812 idResolver.bind(id,o); 813 return id; 814 } 815 816 /** 817 * Looks up the ID table and gets associated object. 818 * 819 * <p> 820 * The exception thrown from {@link Callable#call()} means the unmarshaller should abort 821 * right away. 822 * 823 * @see IDResolver#resolve(String, Class) 824 */ 825 public Callable getObjectFromId( String id, Class targetType ) throws SAXException { 826 return idResolver.resolve(id,targetType); 827 } 828 829 // 830 // 831 // namespace binding maintainance 832 // 833 // 834 private String[] nsBind = new String[16]; 835 private int nsLen=0; 836 837 @Override 838 public void startPrefixMapping( String prefix, String uri ) { 839 if(nsBind.length==nsLen) { 840 // expand the buffer 841 String[] n = new String[nsLen*2]; 842 System.arraycopy(nsBind,0,n,0,nsLen); 843 nsBind=n; 844 } 845 nsBind[nsLen++] = prefix; 846 nsBind[nsLen++] = uri; 847 } 848 @Override 849 public void endPrefixMapping( String prefix ) { 850 nsLen-=2; 851 } 852 private String resolveNamespacePrefix( String prefix ) { 853 if(prefix.equals("xml")) 854 return XMLConstants.XML_NS_URI; 855 856 for( int i=nsLen-2; i>=0; i-=2 ) { 857 if(prefix.equals(nsBind[i])) 858 return nsBind[i+1]; 859 } 860 861 if(environmentNamespaceContext!=null) 862 // temporary workaround until Zephyr fixes 6337180 863 return environmentNamespaceContext.getNamespaceURI(prefix.intern()); 864 865 // by default, the default ns is bound to "". 866 // but allow environmentNamespaceContext to take precedence 867 if(prefix.equals("")) 868 return ""; 869 870 // unresolved. error. 871 return null; 872 } 873 874 /** 875 * Returns a list of prefixes newly declared on the current element. 876 * 877 * @return 878 * A possible zero-length array of prefixes. The default prefix 879 * is represented by the empty string. 880 */ 881 public String[] getNewlyDeclaredPrefixes() { 882 return getPrefixList( current.prev.numNsDecl ); 883 } 884 885 /** 886 * Returns a list of all in-scope prefixes. 887 * 888 * @return 889 * A possible zero-length array of prefixes. The default prefix 890 * is represented by the empty string. 891 */ 892 public String[] getAllDeclaredPrefixes() { 893 return getPrefixList(0); 894 } 895 896 private String[] getPrefixList( int startIndex ) { 897 int size = (current.numNsDecl - startIndex)/2; 898 String[] r = new String[size]; 899 for( int i=0; i<r.length; i++ ) 900 r[i] = nsBind[startIndex+i*2]; 901 return r; 902 } 903 904 // NamespaceContext2 implementation 905 // 906 @Override 907 public Iterator<String> getPrefixes(String uri) { 908 // TODO: could be implemented much faster 909 // wrap it into unmodifiable list so that the remove method 910 // will throw UnsupportedOperationException. 911 return Collections.unmodifiableList( 912 getAllPrefixesInList(uri)).iterator(); 913 } 914 915 private List<String> getAllPrefixesInList(String uri) { 916 List<String> a = new ArrayList<String>(); 917 918 if( uri==null ) 919 throw new IllegalArgumentException(); 920 if( uri.equals(XMLConstants.XML_NS_URI) ) { 921 a.add(XMLConstants.XML_NS_PREFIX); 922 return a; 923 } 924 if( uri.equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI) ) { 925 a.add(XMLConstants.XMLNS_ATTRIBUTE); 926 return a; 927 } 928 929 for( int i=nsLen-2; i>=0; i-=2 ) 930 if(uri.equals(nsBind[i+1])) 931 if( getNamespaceURI(nsBind[i]).equals(nsBind[i+1]) ) 932 // make sure that this prefix is still effective. 933 a.add(nsBind[i]); 934 935 return a; 936 } 937 938 @Override 939 public String getPrefix(String uri) { 940 if( uri==null ) 941 throw new IllegalArgumentException(); 942 if( uri.equals(XMLConstants.XML_NS_URI) ) 943 return XMLConstants.XML_NS_PREFIX; 944 if( uri.equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI) ) 945 return XMLConstants.XMLNS_ATTRIBUTE; 946 947 for( int i=nsLen-2; i>=0; i-=2 ) 948 if(uri.equals(nsBind[i+1])) 949 if( getNamespaceURI(nsBind[i]).equals(nsBind[i+1]) ) 950 // make sure that this prefix is still effective. 951 return nsBind[i]; 952 953 if(environmentNamespaceContext!=null) 954 return environmentNamespaceContext.getPrefix(uri); 955 956 return null; 957 } 958 959 @Override 960 public String getNamespaceURI(String prefix) { 961 if (prefix == null) 962 throw new IllegalArgumentException(); 963 if (prefix.equals(XMLConstants.XMLNS_ATTRIBUTE)) 964 return XMLConstants.XMLNS_ATTRIBUTE_NS_URI; 965 966 return resolveNamespacePrefix(prefix); 967 } 968 969 // 970 // 971 // 972 // scope management 973 // 974 // 975 // 976 private Scope[] scopes = new Scope[16]; 977 /** 978 * Points to the top of the scope stack (=size-1). 979 */ 980 private int scopeTop=0; 981 982 { 983 for( int i=0; i<scopes.length; i++ ) 984 scopes[i] = new Scope(this); 985 } 986 987 /** 988 * Starts a new packing scope. 989 * 990 * <p> 991 * This method allocates a specified number of fresh {@link Scope} objects. 992 * They can be accessed by the {@link #getScope} method until the corresponding 993 * {@link #endScope} method is invoked. 994 * 995 * <p> 996 * A new scope will mask the currently active scope. Only one frame of {@link Scope}s 997 * can be accessed at any given time. 998 * 999 * @param frameSize 1000 * The # of slots to be allocated. 1001 */ 1002 public void startScope(int frameSize) { 1003 scopeTop += frameSize; 1004 1005 // reallocation 1006 if(scopeTop>=scopes.length) { 1007 Scope[] s = new Scope[Math.max(scopeTop+1,scopes.length*2)]; 1008 System.arraycopy(scopes,0,s,0,scopes.length); 1009 for( int i=scopes.length; i<s.length; i++ ) 1010 s[i] = new Scope(this); 1011 scopes = s; 1012 } 1013 } 1014 1015 /** 1016 * Ends the current packing scope. 1017 * 1018 * <p> 1019 * If any packing in progress will be finalized by this method. 1020 * 1021 * @param frameSize 1022 * The same size that gets passed to the {@link #startScope(int)} 1023 * method. 1024 */ 1025 public void endScope(int frameSize) throws SAXException { 1026 try { 1027 for( ; frameSize>0; frameSize--, scopeTop-- ) 1028 scopes[scopeTop].finish(); 1029 } catch (AccessorException e) { 1030 handleError(e); 1031 1032 // the error might have left scopes in inconsistent state, 1033 // so replace them by fresh ones 1034 for( ; frameSize>0; frameSize-- ) 1035 scopes[scopeTop--] = new Scope(this); 1036 } 1037 } 1038 1039 /** 1040 * Gets the currently active {@link Scope}. 1041 * 1042 * @param offset 1043 * a number between [0,frameSize) 1044 * 1045 * @return 1046 * always a valid {@link Scope} object. 1047 */ 1048 public Scope getScope(int offset) { 1049 return scopes[scopeTop-offset]; 1050 } 1051 1052 // 1053 // 1054 // 1055 // 1056 // 1057 // 1058 // 1059 1060 private static final Loader DEFAULT_ROOT_LOADER = new DefaultRootLoader(); 1061 private static final Loader EXPECTED_TYPE_ROOT_LOADER = new ExpectedTypeRootLoader(); 1062 1063 /** 1064 * Root loader that uses the tag name and possibly its @xsi:type 1065 * to decide how to start unmarshalling. 1066 */ 1067 private static final class DefaultRootLoader extends Loader implements Receiver { 1068 /** 1069 * Receives the root element and determines how to start 1070 * unmarshalling. 1071 */ 1072 @Override 1073 public void childElement(UnmarshallingContext.State state, TagName ea) throws SAXException { 1074 Loader loader = state.getContext().selectRootLoader(state,ea); 1075 if(loader!=null) { 1076 state.loader = loader; 1077 state.receiver = this; 1078 return; 1079 } 1080 1081 // the registry doesn't know about this element. 1082 // try its xsi:type 1083 JaxBeanInfo beanInfo = XsiTypeLoader.parseXsiType(state, ea, null); 1084 if(beanInfo==null) { 1085 // we don't even know its xsi:type 1086 reportUnexpectedChildElement(ea,false); 1087 return; 1088 } 1089 1090 state.loader = beanInfo.getLoader(null,false); 1091 state.prev.backup = new JAXBElement<Object>(ea.createQName(),Object.class,null); 1092 state.receiver = this; 1093 } 1094 1095 @Override 1096 public Collection<QName> getExpectedChildElements() { 1097 return getInstance().getJAXBContext().getValidRootNames(); 1098 } 1099 1100 @Override 1101 public void receive(State state, Object o) { 1102 if(state.backup!=null) { 1103 ((JAXBElement<Object>)state.backup).setValue(o); 1104 o = state.backup; 1105 } 1106 if (state.nil) { 1107 ((JAXBElement<Object>)o).setNil(true); 1108 } 1109 state.getContext().result = o; 1110 } 1111 } 1112 1113 /** 1114 * Root loader that uses {@link UnmarshallingContext#expectedType} 1115 * to decide how to start unmarshalling. 1116 */ 1117 private static final class ExpectedTypeRootLoader extends Loader implements Receiver { 1118 /** 1119 * Receives the root element and determines how to start 1120 * unmarshalling. 1121 */ 1122 @Override 1123 public void childElement(UnmarshallingContext.State state, TagName ea) { 1124 UnmarshallingContext context = state.getContext(); 1125 1126 // unmarshals the specified type 1127 QName qn = new QName(ea.uri,ea.local); 1128 state.prev.target = new JAXBElement(qn,context.expectedType.jaxbType,null,null); 1129 state.receiver = this; 1130 // this is bit wasteful, as in theory we should have each expectedType keep 1131 // nillable version --- but that increases the combination from two to four, 1132 // which adds the resident memory footprint. Since XsiNilLoader is small, 1133 // I intentionally allocate a new instance freshly. 1134 state.loader = new XsiNilLoader(context.expectedType.getLoader(null,true)); 1135 } 1136 1137 @Override 1138 public void receive(State state, Object o) { 1139 JAXBElement e = (JAXBElement)state.target; 1140 e.setValue(o); 1141 state.getContext().recordOuterPeer(e); 1142 state.getContext().result = e; 1143 } 1144 } 1145 1146 // 1147 // in-place unmarshalling related capabilities 1148 // 1149 /** 1150 * Notifies the context about the inner peer of the current element. 1151 * 1152 * <p> 1153 * If the unmarshalling is building the association, the context 1154 * will use this information. Otherwise it will be just ignored. 1155 */ 1156 public void recordInnerPeer(Object innerPeer) { 1157 if(assoc!=null) 1158 assoc.addInner(currentElement,innerPeer); 1159 } 1160 1161 /** 1162 * Gets the inner peer JAXB object associated with the current element. 1163 * 1164 * @return 1165 * null if the current element doesn't have an inner peer, 1166 * or if we are not doing the in-place unmarshalling. 1167 */ 1168 public Object getInnerPeer() { 1169 if(assoc!=null && isInplaceMode) 1170 return assoc.getInnerPeer(currentElement); 1171 else 1172 return null; 1173 } 1174 1175 /** 1176 * Notifies the context about the outer peer of the current element. 1177 * 1178 * <p> 1179 * If the unmarshalling is building the association, the context 1180 * will use this information. Otherwise it will be just ignored. 1181 */ 1182 public void recordOuterPeer(Object outerPeer) { 1183 if(assoc!=null) 1184 assoc.addOuter(currentElement,outerPeer); 1185 } 1186 1187 /** 1188 * Gets the outer peer JAXB object associated with the current element. 1189 * 1190 * @return 1191 * null if the current element doesn't have an inner peer, 1192 * or if we are not doing the in-place unmarshalling. 1193 */ 1194 public Object getOuterPeer() { 1195 if(assoc!=null && isInplaceMode) 1196 return assoc.getOuterPeer(currentElement); 1197 else 1198 return null; 1199 } 1200 1201 /** 1202 * Gets the xmime:contentType value for the current object. 1203 * 1204 * @see JAXBContextImpl#getXMIMEContentType(Object) 1205 */ 1206 public String getXMIMEContentType() { 1207 /* 1208 this won't work when the class is like 1209 1210 class Foo { 1211 @XmlValue Image img; 1212 } 1213 1214 because the target will return Foo, not the class enclosing Foo 1215 which will have xmime:contentType 1216 */ 1217 Object t = current.target; 1218 if(t==null) return null; 1219 return getJAXBContext().getXMIMEContentType(t); 1220 } 1221 1222 /** 1223 * When called from within the realm of the unmarshaller, this method 1224 * returns the current {@link UnmarshallingContext} in charge. 1225 */ 1226 public static UnmarshallingContext getInstance() { 1227 return (UnmarshallingContext) Coordinator._getInstance(); 1228 } 1229 1230 /** 1231 * Allows to access elements which are expected in current state. 1232 * Useful for getting elements for current parent. 1233 * 1234 * @return 1235 */ 1236 public Collection<QName> getCurrentExpectedElements() { 1237 pushCoordinator(); 1238 try { 1239 State s = getCurrentState(); 1240 Loader l = s.loader; 1241 return (l != null) ? l.getExpectedChildElements() : null; 1242 } finally { 1243 popCoordinator(); 1244 } 1245 } 1246 1247 /** 1248 * Allows to access attributes which are expected in current state. 1249 * Useful for getting attributes for current parent. 1250 * 1251 * @return 1252 */ 1253 public Collection<QName> getCurrentExpectedAttributes() { 1254 pushCoordinator(); 1255 try { 1256 State s = getCurrentState(); 1257 Loader l = s.loader; 1258 return (l != null) ? l.getExpectedAttributes() : null; 1259 } finally { 1260 popCoordinator(); 1261 } 1262 } 1263 1264 /** 1265 * Gets StructureLoader if used as loader. 1266 * Useful when determining if element is mixed or not. 1267 * 1268 */ 1269 public StructureLoader getStructureLoader() { 1270 if(current.loader instanceof StructureLoader) 1271 return (StructureLoader)current.loader; 1272 1273 return null; 1274 } 1275 1276 /** 1277 * Based on current {@link Logger} {@link Level} and errorCounter value determines if error should be reported. 1278 * 1279 * If the method called and return true it is expected that error will be reported. And that's why 1280 * errorCounter is automatically decremented during the check. 1281 * 1282 * NOT THREAD SAFE!!! In case of heave concurrency access several additional errors could be reported. It's not expected to be the 1283 * problem. Otherwise add synchronization here. 1284 * 1285 * @return true in case if {@link Level#FINEST} is set OR we haven't exceed errors reporting limit. 1286 */ 1287 public boolean shouldErrorBeReported() throws SAXException { 1288 if (logger.isLoggable(Level.FINEST)) 1289 return true; 1290 1291 if (errorsCounter >= 0) { 1292 --errorsCounter; 1293 if (errorsCounter == 0) // it's possible to miss this because of concurrency. If required add synchronization here 1294 handleEvent(new ValidationEventImpl(ValidationEvent.WARNING, Messages.ERRORS_LIMIT_EXCEEDED.format(), 1295 getLocator().getLocation(), null), true); 1296 } 1297 return errorsCounter >= 0; 1298 } 1299 }