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