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