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
26package com.sun.xml.internal.bind.v2.runtime.unmarshaller;
27
28import java.lang.reflect.InvocationTargetException;
29import java.lang.reflect.Method;
30import java.util.ArrayList;
31import java.util.Collection;
32import java.util.Collections;
33import java.util.HashMap;
34import java.util.Iterator;
35import java.util.List;
36import java.util.Map;
37import java.util.concurrent.Callable;
38import java.util.logging.Level;
39import java.util.logging.Logger;
40
41import javax.xml.XMLConstants;
42import javax.xml.bind.JAXBElement;
43import javax.xml.bind.UnmarshalException;
44import javax.xml.bind.Unmarshaller;
45import javax.xml.bind.ValidationEvent;
46import javax.xml.bind.ValidationEventHandler;
47import javax.xml.bind.ValidationEventLocator;
48import javax.xml.bind.helpers.ValidationEventImpl;
49import javax.xml.namespace.NamespaceContext;
50import javax.xml.namespace.QName;
51
52import com.sun.istack.internal.NotNull;
53import com.sun.istack.internal.Nullable;
54import com.sun.istack.internal.SAXParseException2;
55import com.sun.xml.internal.bind.IDResolver;
56import com.sun.xml.internal.bind.Util;
57import com.sun.xml.internal.bind.api.AccessorException;
58import com.sun.xml.internal.bind.api.ClassResolver;
59import com.sun.xml.internal.bind.unmarshaller.InfosetScanner;
60import com.sun.xml.internal.bind.v2.ClassFactory;
61import com.sun.xml.internal.bind.v2.runtime.AssociationMap;
62import com.sun.xml.internal.bind.v2.runtime.Coordinator;
63import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl;
64import com.sun.xml.internal.bind.v2.runtime.JaxBeanInfo;
65import java.util.logging.Level;
66import java.util.logging.Logger;
67
68import org.xml.sax.ErrorHandler;
69import org.xml.sax.Locator;
70import org.xml.sax.SAXException;
71import 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 */
82public 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}
1348