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