325N/A/*
325N/A * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved.
325N/A * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
325N/A *
325N/A * This code is free software; you can redistribute it and/or modify it
325N/A * under the terms of the GNU General Public License version 2 only, as
325N/A * published by the Free Software Foundation. Oracle designates this
325N/A * particular file as subject to the "Classpath" exception as provided
325N/A * by Oracle in the LICENSE file that accompanied this code.
325N/A *
325N/A * This code is distributed in the hope that it will be useful, but WITHOUT
325N/A * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
325N/A * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
325N/A * version 2 for more details (a copy is included in the LICENSE file that
325N/A * accompanied this code).
325N/A *
325N/A * You should have received a copy of the GNU General Public License version
325N/A * 2 along with this work; if not, write to the Free Software Foundation,
325N/A * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
325N/A *
325N/A * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
325N/A * or visit www.oracle.com if you need additional information or have any
325N/A * questions.
325N/A */
325N/A
325N/Apackage com.sun.xml.internal.bind.v2.runtime.unmarshaller;
325N/A
325N/Aimport java.lang.reflect.InvocationTargetException;
325N/Aimport java.lang.reflect.Method;
325N/Aimport java.util.ArrayList;
325N/Aimport java.util.Collection;
325N/Aimport java.util.Collections;
325N/Aimport java.util.HashMap;
325N/Aimport java.util.Iterator;
325N/Aimport java.util.List;
325N/Aimport java.util.Map;
325N/Aimport java.util.concurrent.Callable;
325N/A
325N/Aimport javax.xml.XMLConstants;
325N/Aimport javax.xml.bind.JAXBElement;
325N/Aimport javax.xml.bind.UnmarshalException;
325N/Aimport javax.xml.bind.Unmarshaller;
325N/Aimport javax.xml.bind.ValidationEvent;
325N/Aimport javax.xml.bind.ValidationEventHandler;
325N/Aimport javax.xml.bind.ValidationEventLocator;
325N/Aimport javax.xml.bind.helpers.ValidationEventImpl;
325N/Aimport javax.xml.namespace.NamespaceContext;
325N/Aimport javax.xml.namespace.QName;
325N/A
325N/Aimport com.sun.istack.internal.NotNull;
325N/Aimport com.sun.istack.internal.Nullable;
325N/Aimport com.sun.istack.internal.SAXParseException2;
325N/Aimport com.sun.xml.internal.bind.IDResolver;
325N/Aimport com.sun.xml.internal.bind.api.AccessorException;
325N/Aimport com.sun.xml.internal.bind.api.ClassResolver;
325N/Aimport com.sun.xml.internal.bind.unmarshaller.InfosetScanner;
325N/Aimport com.sun.xml.internal.bind.v2.ClassFactory;
325N/Aimport com.sun.xml.internal.bind.v2.runtime.AssociationMap;
325N/Aimport com.sun.xml.internal.bind.v2.runtime.Coordinator;
325N/Aimport com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl;
325N/Aimport com.sun.xml.internal.bind.v2.runtime.JaxBeanInfo;
325N/A
325N/Aimport org.xml.sax.ErrorHandler;
325N/Aimport org.xml.sax.SAXException;
325N/Aimport org.xml.sax.helpers.LocatorImpl;
325N/A
325N/A/**
325N/A * Center of the unmarshalling.
325N/A *
325N/A * <p>
325N/A * This object is responsible for coordinating {@link Loader}s to
325N/A * perform the whole unmarshalling.
325N/A *
325N/A * @author Kohsuke Kawaguchi
325N/A */
325N/Apublic final class UnmarshallingContext extends Coordinator
325N/A implements NamespaceContext, ValidationEventHandler, ErrorHandler, XmlVisitor, XmlVisitor.TextPredictor {
325N/A
325N/A /**
325N/A * Root state.
325N/A */
325N/A private final State root;
325N/A
325N/A /**
325N/A * The currently active state.
325N/A */
325N/A private State current;
325N/A
325N/A private static final LocatorEx DUMMY_INSTANCE;
325N/A
325N/A static {
325N/A LocatorImpl loc = new LocatorImpl();
325N/A loc.setPublicId(null);
325N/A loc.setSystemId(null);
325N/A loc.setLineNumber(-1);
325N/A loc.setColumnNumber(-1);
325N/A DUMMY_INSTANCE = new LocatorExWrapper(loc);
325N/A }
325N/A
325N/A private @NotNull LocatorEx locator = DUMMY_INSTANCE;
325N/A
325N/A /** Root object that is being unmarshalled. */
325N/A private Object result;
325N/A
325N/A /**
325N/A * If non-null, this unmarshaller will unmarshal {@code JAXBElement<EXPECTEDTYPE>}
325N/A * regardless of the tag name, as opposed to deciding the root object by using
325N/A * the tag name.
325N/A *
325N/A * The property has a package-level access, because we cannot copy this value
325N/A * to {@link UnmarshallingContext} when it is created. The property
325N/A * on {@link Unmarshaller} could be changed after the handler is created.
325N/A */
325N/A private JaxBeanInfo expectedType;
325N/A
325N/A /**
325N/A * Handles ID/IDREF.
325N/A */
325N/A private IDResolver idResolver;
325N/A
325N/A /**
325N/A * This flag is set to true at the startDocument event
325N/A * and false at the endDocument event.
325N/A *
325N/A * Until the first document is unmarshalled, we don't
325N/A * want to return an object. So this variable is initialized
325N/A * to true.
325N/A */
325N/A private boolean isUnmarshalInProgress = true;
325N/A private boolean aborted = false;
325N/A
325N/A public final UnmarshallerImpl parent;
325N/A
325N/A /**
325N/A * If the unmarshaller is doing associative unmarshalling,
325N/A * this field is initialized to non-null.
325N/A */
325N/A private final AssociationMap assoc;
325N/A
325N/A /**
325N/A * Indicates whether we are doing in-place unmarshalling
325N/A * or not.
325N/A *
325N/A * <p>
325N/A * This flag is unused when {@link #assoc}==null.
325N/A * If it's non-null, then <tt>true</tt> indicates
325N/A * that we are doing in-place associative unmarshalling.
325N/A * If <tt>false</tt>, then we are doing associative unmarshalling
325N/A * without object reuse.
325N/A */
325N/A private boolean isInplaceMode;
325N/A
325N/A /**
325N/A * This object is consulted to get the element object for
325N/A * the current element event.
325N/A *
325N/A * This is used when we are building an association map.
325N/A */
325N/A private InfosetScanner scanner;
325N/A
325N/A private Object currentElement;
325N/A
325N/A /**
325N/A * @see XmlVisitor#startDocument(LocatorEx, NamespaceContext)
325N/A */
325N/A private NamespaceContext environmentNamespaceContext;
325N/A
325N/A /**
325N/A * Used to discover additional classes when we hit unknown elements/types.
325N/A */
325N/A public @Nullable ClassResolver classResolver;
325N/A
325N/A /**
325N/A * User-supplied {@link ClassLoader} for converting name to {@link Class}.
325N/A * For backward compatibility, when null, use thread context classloader.
325N/A */
325N/A public @Nullable ClassLoader classLoader;
325N/A
325N/A /**
325N/A * State information for each element.
325N/A */
325N/A public final class State {
325N/A /**
325N/A * Loader that owns this element.
325N/A */
325N/A public Loader loader;
325N/A /**
325N/A * Once {@link #loader} is completed, this receiver
325N/A * receives the result.
325N/A */
325N/A public Receiver receiver;
325N/A
325N/A public Intercepter intercepter;
325N/A
325N/A
325N/A /**
325N/A * Object being unmarshalled by this {@link #loader}.
325N/A */
325N/A public Object target;
325N/A
325N/A /**
325N/A * Hack for making JAXBElement unmarshalling work.
325N/A *
325N/A * <p>
325N/A * While the unmarshalling is in progress, the {@link #target} field stores the object being unmarshalled.
325N/A * This makes it convenient to keep track of the unmarshalling activity in context of XML infoset, but
325N/A * since there's only one {@link State} per element, this mechanism only works when there's one object
325N/A * per element, which breaks down when we have {@link JAXBElement}, since the presence of JAXBElement
325N/A * requires that we have two objects unmarshalled (a JAXBElement X and a value object Y bound to an XML type.)
325N/A *
325N/A * <p>
325N/A * So to make room for storing both, this {@link #backup} field is used. When we create X instance
325N/A * in the above example, we set that to {@code state.prev.target} and displace its old value to
325N/A * {@code state.prev.backup} (where Y goes to {@code state.target}.) Upon the completion of the unmarshalling
325N/A * of Y, we revert this.
325N/A *
325N/A * <p>
325N/A * While this attributes X incorrectly to its parent element, this preserves the parent/child
325N/A * relationship between unmarshalled objects and {@link State} parent/child relationship, and
325N/A * it thereby makes {@link Receiver} mechanism simpler.
325N/A *
325N/A * <p>
325N/A * Yes, I know this is a hack, and no, I'm not proud of it.
325N/A *
325N/A * @see ElementBeanInfoImpl.IntercepterLoader#startElement(State, TagName)
325N/A * @see ElementBeanInfoImpl.IntercepterLoader#intercept(State, Object)
325N/A */
325N/A public Object backup;
325N/A
325N/A /**
325N/A * Number of {@link UnmarshallingContext#nsBind}s declared thus far.
325N/A * (The value of {@link UnmarshallingContext#nsLen} when this state is pushed.
325N/A */
325N/A private int numNsDecl;
325N/A
325N/A /**
325N/A * If this element has an element default value.
325N/A *
325N/A * This should be set by either a parent {@link Loader} when
325N/A * {@link Loader#childElement(State, TagName)} is called
325N/A * or by a child {@link Loader} when
325N/A * {@link Loader#startElement(State, TagName)} is called.
325N/A */
325N/A public String elementDefaultValue;
325N/A
325N/A /**
325N/A * {@link State} for the parent element
325N/A *
325N/A * {@link State} objects form a doubly linked list.
325N/A */
325N/A public State prev;
325N/A private State next;
325N/A
325N/A public boolean nil = false;
325N/A
325N/A /**
325N/A * Gets the context.
325N/A */
325N/A public UnmarshallingContext getContext() {
325N/A return UnmarshallingContext.this;
325N/A }
325N/A
325N/A private State(State prev) {
325N/A this.prev = prev;
325N/A if(prev!=null)
325N/A prev.next = this;
325N/A }
325N/A
325N/A private void push() {
325N/A if(next==null)
325N/A allocateMoreStates();
325N/A State n = next;
325N/A n.numNsDecl = nsLen;
325N/A current = n;
325N/A }
325N/A
325N/A private void pop() {
325N/A assert prev!=null;
325N/A loader = null;
325N/A nil = false;
325N/A receiver = null;
325N/A intercepter = null;
325N/A elementDefaultValue = null;
325N/A target = null;
325N/A current = prev;
325N/A }
325N/A }
325N/A
325N/A /**
325N/A * Stub to the user-specified factory method.
325N/A */
325N/A private static class Factory {
325N/A private final Object factorInstance;
325N/A private final Method method;
325N/A
325N/A public Factory(Object factorInstance, Method method) {
325N/A this.factorInstance = factorInstance;
325N/A this.method = method;
325N/A }
325N/A
325N/A public Object createInstance() throws SAXException {
325N/A try {
325N/A return method.invoke(factorInstance);
325N/A } catch (IllegalAccessException e) {
325N/A getInstance().handleError(e,false);
325N/A } catch (InvocationTargetException e) {
325N/A getInstance().handleError(e,false);
325N/A }
325N/A return null; // can never be executed
325N/A }
325N/A }
325N/A
325N/A
325N/A /**
325N/A * Creates a new unmarshaller.
325N/A *
325N/A * @param assoc
325N/A * Must be both non-null when the unmarshaller does the
325N/A * in-place unmarshalling. Otherwise must be both null.
325N/A */
325N/A public UnmarshallingContext( UnmarshallerImpl _parent, AssociationMap assoc) {
325N/A this.parent = _parent;
325N/A this.assoc = assoc;
325N/A this.root = this.current = new State(null);
325N/A allocateMoreStates();
325N/A }
325N/A
325N/A public void reset(InfosetScanner scanner,boolean isInplaceMode, JaxBeanInfo expectedType, IDResolver idResolver) {
325N/A this.scanner = scanner;
325N/A this.isInplaceMode = isInplaceMode;
325N/A this.expectedType = expectedType;
325N/A this.idResolver = idResolver;
325N/A }
325N/A
325N/A public JAXBContextImpl getJAXBContext() {
325N/A return parent.context;
325N/A }
325N/A
325N/A public State getCurrentState() {
325N/A return current;
325N/A }
325N/A
325N/A /**
325N/A * On top of {@link JAXBContextImpl#selectRootLoader(State, TagName)},
325N/A * this method also consults {@link ClassResolver}.
325N/A *
325N/A * @throws SAXException
325N/A * if {@link ValidationEventHandler} reported a failure.
325N/A */
325N/A public Loader selectRootLoader(State state, TagName tag) throws SAXException {
325N/A try {
325N/A Loader l = getJAXBContext().selectRootLoader(state, tag);
325N/A if(l!=null) return l;
325N/A
325N/A if(classResolver!=null) {
325N/A Class<?> clazz = classResolver.resolveElementName(tag.uri, tag.local);
325N/A if(clazz!=null) {
325N/A JAXBContextImpl enhanced = getJAXBContext().createAugmented(clazz);
325N/A JaxBeanInfo<?> bi = enhanced.getBeanInfo(clazz);
325N/A return bi.getLoader(enhanced,true);
325N/A }
325N/A }
325N/A } catch (RuntimeException e) {
325N/A throw e;
325N/A } catch (Exception e) {
325N/A handleError(e);
325N/A }
325N/A
325N/A return null;
325N/A }
325N/A
325N/A /**
325N/A * Allocates a few more {@link State}s.
325N/A *
325N/A * Allocating multiple {@link State}s at once allows those objects
325N/A * to be allocated near each other, which reduces the working set
325N/A * of CPU. It improves the chance the relevant data is in the cache.
325N/A */
325N/A private void allocateMoreStates() {
325N/A // this method should be used only when we run out of a state.
325N/A assert current.next==null;
325N/A
325N/A State s = current;
325N/A for( int i=0; i<8; i++ )
325N/A s = new State(s);
325N/A }
325N/A
325N/A public void clearStates() {
325N/A State last = current;
325N/A while (last.next != null) last = last.next;
325N/A while (last.prev != null) {
325N/A last.loader = null;
325N/A last.nil = false;
325N/A last.receiver = null;
325N/A last.intercepter = null;
325N/A last.elementDefaultValue = null;
325N/A last.target = null;
325N/A last = last.prev;
325N/A last.next.prev = null;
325N/A last.next = null;
325N/A }
325N/A current = last;
325N/A }
325N/A
325N/A /**
325N/A * User-specified factory methods.
325N/A */
325N/A private final Map<Class,Factory> factories = new HashMap<Class, Factory>();
325N/A
325N/A public void setFactories(Object factoryInstances) {
325N/A factories.clear();
325N/A if(factoryInstances==null) {
325N/A return;
325N/A }
325N/A if(factoryInstances instanceof Object[]) {
325N/A for( Object factory : (Object[])factoryInstances ) {
325N/A // look for all the public methods inlcuding derived ones
325N/A addFactory(factory);
325N/A }
325N/A } else {
325N/A addFactory(factoryInstances);
325N/A }
325N/A }
325N/A
325N/A private void addFactory(Object factory) {
325N/A for( Method m : factory.getClass().getMethods() ) {
325N/A // look for methods whose signature is T createXXX()
325N/A if(!m.getName().startsWith("create"))
325N/A continue;
325N/A if(m.getParameterTypes().length>0)
325N/A continue;
325N/A
325N/A Class type = m.getReturnType();
325N/A
325N/A factories.put(type,new Factory(factory,m));
325N/A }
325N/A }
325N/A
325N/A public void startDocument(LocatorEx locator, NamespaceContext nsContext) throws SAXException {
325N/A if(locator!=null)
325N/A this.locator = locator;
325N/A this.environmentNamespaceContext = nsContext;
325N/A // reset the object
325N/A result = null;
325N/A current = root;
325N/A
325N/A patchersLen=0;
325N/A aborted = false;
325N/A isUnmarshalInProgress = true;
325N/A nsLen=0;
325N/A
325N/A setThreadAffinity();
325N/A
325N/A if(expectedType!=null)
325N/A root.loader = EXPECTED_TYPE_ROOT_LOADER;
325N/A else
325N/A root.loader = DEFAULT_ROOT_LOADER;
325N/A
325N/A idResolver.startDocument(this);
325N/A }
325N/A
325N/A public void startElement(TagName tagName) throws SAXException {
325N/A pushCoordinator();
325N/A try {
325N/A _startElement(tagName);
325N/A } finally {
325N/A popCoordinator();
325N/A }
325N/A }
325N/A
325N/A private void _startElement(TagName tagName) throws SAXException {
325N/A // remember the current element if we are interested in it.
325N/A // because the inner peer might not be found while we consume
325N/A // the enter element token, we need to keep this information
325N/A // longer than this callback. That's why we assign it to a field.
325N/A if( assoc!=null )
325N/A currentElement = scanner.getCurrentElement();
325N/A
325N/A Loader h = current.loader;
325N/A current.push();
325N/A
325N/A // tell the parent about the new child
325N/A h.childElement(current,tagName);
325N/A assert current.loader!=null; // the childElement should register this
325N/A // and tell the new child that you are activated
325N/A current.loader.startElement(current,tagName);
325N/A }
325N/A
325N/A public void text(CharSequence pcdata) throws SAXException {
325N/A State cur = current;
325N/A pushCoordinator();
325N/A try {
325N/A if(cur.elementDefaultValue!=null) {
325N/A if(pcdata.length()==0) {
325N/A // send the default value into the unmarshaller instead
325N/A pcdata = cur.elementDefaultValue;
325N/A }
325N/A }
325N/A cur.loader.text(cur,pcdata);
325N/A } finally {
325N/A popCoordinator();
325N/A }
325N/A }
325N/A
325N/A public final void endElement(TagName tagName) throws SAXException {
325N/A pushCoordinator();
325N/A try {
325N/A State child = current;
325N/A
325N/A // tell the child that your time is up
325N/A child.loader.leaveElement(child,tagName);
325N/A
325N/A // child.pop will erase them so store them now
325N/A Object target = child.target;
325N/A Receiver recv = child.receiver;
325N/A Intercepter intercepter = child.intercepter;
325N/A child.pop();
325N/A
325N/A // then let the parent know
325N/A if(intercepter!=null)
325N/A target = intercepter.intercept(current,target);
325N/A if(recv!=null)
325N/A recv.receive(current,target);
325N/A } finally {
325N/A popCoordinator();
325N/A }
325N/A }
325N/A
325N/A public void endDocument() throws SAXException {
325N/A runPatchers();
325N/A idResolver.endDocument();
325N/A
325N/A isUnmarshalInProgress = false;
325N/A currentElement = null;
325N/A locator = DUMMY_INSTANCE;
325N/A environmentNamespaceContext = null;
325N/A
325N/A // at the successful completion, scope must be all closed
325N/A assert root==current;
325N/A
325N/A resetThreadAffinity();
325N/A }
325N/A
325N/A /**
325N/A * You should be always calling this through {@link TextPredictor}.
325N/A */
325N/A @Deprecated
325N/A public boolean expectText() {
325N/A return current.loader.expectText;
325N/A }
325N/A
325N/A /**
325N/A * You should be always getting {@link TextPredictor} from {@link XmlVisitor}.
325N/A */
325N/A @Deprecated
325N/A public TextPredictor getPredictor() {
325N/A return this;
325N/A }
325N/A
325N/A public UnmarshallingContext getContext() {
325N/A return this;
325N/A }
325N/A
325N/A /**
325N/A * Gets the result of the unmarshalling
325N/A */
325N/A public Object getResult() throws UnmarshalException {
325N/A if(isUnmarshalInProgress)
325N/A throw new IllegalStateException();
325N/A
325N/A if(!aborted) return result;
325N/A
325N/A // there was an error.
325N/A throw new UnmarshalException((String)null);
325N/A }
325N/A
325N/A void clearResult() {
325N/A if (isUnmarshalInProgress) {
325N/A throw new IllegalStateException();
325N/A }
325N/A result = null;
325N/A }
325N/A
325N/A /**
325N/A * Creates a new instance of the specified class.
325N/A * In the unmarshaller, we need to check the user-specified factory class.
325N/A */
325N/A public Object createInstance( Class<?> clazz ) throws SAXException {
325N/A if(!factories.isEmpty()) {
325N/A Factory factory = factories.get(clazz);
325N/A if(factory!=null)
325N/A return factory.createInstance();
325N/A }
325N/A return ClassFactory.create(clazz);
325N/A }
325N/A
325N/A /**
325N/A * Creates a new instance of the specified class.
325N/A * In the unmarshaller, we need to check the user-specified factory class.
325N/A */
325N/A public Object createInstance( JaxBeanInfo beanInfo ) throws SAXException {
325N/A if(!factories.isEmpty()) {
325N/A Factory factory = factories.get(beanInfo.jaxbType);
325N/A if(factory!=null)
325N/A return factory.createInstance();
325N/A }
325N/A try {
325N/A return beanInfo.createInstance(this);
325N/A } catch (IllegalAccessException e) {
325N/A Loader.reportError("Unable to create an instance of "+beanInfo.jaxbType.getName(),e,false);
325N/A } catch (InvocationTargetException e) {
325N/A Loader.reportError("Unable to create an instance of "+beanInfo.jaxbType.getName(),e,false);
325N/A } catch (InstantiationException e) {
325N/A Loader.reportError("Unable to create an instance of "+beanInfo.jaxbType.getName(),e,false);
325N/A }
325N/A return null; // can never be here
325N/A }
325N/A
325N/A
325N/A
325N/A//
325N/A//
325N/A// error handling
325N/A//
325N/A//
325N/A
325N/A /**
325N/A * Reports an error to the user, and asks if s/he wants
325N/A * to recover. If the canRecover flag is false, regardless
325N/A * of the client instruction, an exception will be thrown.
325N/A *
325N/A * Only if the flag is true and the user wants to recover from an error,
325N/A * the method returns normally.
325N/A *
325N/A * The thrown exception will be catched by the unmarshaller.
325N/A */
325N/A public void handleEvent(ValidationEvent event, boolean canRecover ) throws SAXException {
325N/A ValidationEventHandler eventHandler = parent.getEventHandler();
325N/A
325N/A boolean recover = eventHandler.handleEvent(event);
325N/A
325N/A // if the handler says "abort", we will not return the object
325N/A // from the unmarshaller.getResult()
325N/A if(!recover) aborted = true;
325N/A
325N/A if( !canRecover || !recover )
325N/A throw new SAXParseException2( event.getMessage(), locator,
325N/A new UnmarshalException(
325N/A event.getMessage(),
325N/A event.getLinkedException() ) );
325N/A }
325N/A
325N/A public boolean handleEvent(ValidationEvent event) {
325N/A try {
325N/A // if the handler says "abort", we will not return the object.
325N/A boolean recover = parent.getEventHandler().handleEvent(event);
325N/A if(!recover) aborted = true;
325N/A return recover;
325N/A } catch( RuntimeException re ) {
325N/A // if client event handler causes a runtime exception, then we
325N/A // have to return false.
325N/A return false;
325N/A }
325N/A }
325N/A
325N/A /**
325N/A * Reports an exception found during the unmarshalling to the user.
325N/A * This method is a convenience method that calls into
325N/A * {@link #handleEvent(ValidationEvent, boolean)}
325N/A */
325N/A public void handleError(Exception e) throws SAXException {
325N/A handleError(e,true);
325N/A }
325N/A
325N/A public void handleError(Exception e,boolean canRecover) throws SAXException {
325N/A handleEvent(new ValidationEventImpl(ValidationEvent.ERROR,e.getMessage(),locator.getLocation(),e),canRecover);
325N/A }
325N/A
325N/A public void handleError(String msg) {
325N/A handleEvent(new ValidationEventImpl(ValidationEvent.ERROR,msg,locator.getLocation()));
325N/A }
325N/A
325N/A protected ValidationEventLocator getLocation() {
325N/A return locator.getLocation();
325N/A }
325N/A
325N/A /**
325N/A * Gets the current source location information in SAX {@link Locator}.
325N/A * <p>
325N/A * Sometimes the unmarshaller works against a different kind of XML source,
325N/A * making this information meaningless.
325N/A */
325N/A public LocatorEx getLocator() { return locator; }
325N/A
325N/A /**
325N/A * Called when there's no corresponding ID value.
325N/A */
325N/A public void errorUnresolvedIDREF(Object bean, String idref, LocatorEx loc) throws SAXException {
325N/A handleEvent( new ValidationEventImpl(
325N/A ValidationEvent.ERROR,
325N/A Messages.UNRESOLVED_IDREF.format(idref),
325N/A loc.getLocation()), true );
325N/A }
325N/A
325N/A
325N/A//
325N/A//
325N/A// ID/IDREF related code
325N/A//
325N/A//
325N/A /**
325N/A * Submitted patchers in the order they've submitted.
325N/A * Many XML vocabulary doesn't use ID/IDREF at all, so we
325N/A * initialize it with null.
325N/A */
325N/A private Patcher[] patchers = null;
325N/A private int patchersLen = 0;
325N/A
325N/A /**
325N/A * Adds a job that will be executed at the last of the unmarshalling.
325N/A * This method is used to support ID/IDREF feature, but it can be used
325N/A * for other purposes as well.
325N/A *
325N/A * @param job
325N/A * The run method of this object is called.
325N/A */
325N/A public void addPatcher( Patcher job ) {
325N/A // re-allocate buffer if necessary
325N/A if( patchers==null )
325N/A patchers = new Patcher[32];
325N/A if( patchers.length == patchersLen ) {
325N/A Patcher[] buf = new Patcher[patchersLen*2];
325N/A System.arraycopy(patchers,0,buf,0,patchersLen);
325N/A patchers = buf;
325N/A }
325N/A patchers[patchersLen++] = job;
325N/A }
325N/A
325N/A /** Executes all the patchers. */
325N/A private void runPatchers() throws SAXException {
325N/A if( patchers!=null ) {
325N/A for( int i=0; i<patchersLen; i++ ) {
325N/A patchers[i].run();
325N/A patchers[i] = null; // free memory
325N/A }
325N/A }
325N/A }
325N/A
325N/A /**
325N/A * Adds the object which is currently being unmarshalled
325N/A * to the ID table.
325N/A *
325N/A * @return
325N/A * Returns the value passed as the parameter.
325N/A * This is a hack, but this makes it easier for ID
325N/A * transducer to do its job.
325N/A */
325N/A // TODO: what shall we do if the ID is already declared?
325N/A //
325N/A // throwing an exception is one way. Overwriting the previous one
325N/A // is another way. The latter allows us to process invalid documents,
325N/A // while the former makes it impossible to handle them.
325N/A //
325N/A // I prefer to be flexible in terms of invalid document handling,
325N/A // so chose not to throw an exception.
325N/A //
325N/A // I believe this is an implementation choice, not the spec issue.
325N/A // -kk
325N/A public String addToIdTable( String id ) throws SAXException {
325N/A // Hmm...
325N/A // in cases such as when ID is used as an attribute, or as @XmlValue
325N/A // the target wilil be current.target.
325N/A // but in some other cases, such as when ID is used as a child element
325N/A // or a value of JAXBElement, it's current.prev.target.
325N/A // I don't know if this detection logic is complete
325N/A Object o = current.target;
325N/A if(o==null)
325N/A o = current.prev.target;
325N/A idResolver.bind(id,o);
325N/A return id;
325N/A }
325N/A
325N/A /**
325N/A * Looks up the ID table and gets associated object.
325N/A *
325N/A * <p>
325N/A * The exception thrown from {@link Callable#call()} means the unmarshaller should abort
325N/A * right away.
325N/A *
325N/A * @see IDResolver#resolve(String, Class)
325N/A */
325N/A public Callable getObjectFromId( String id, Class targetType ) throws SAXException {
325N/A return idResolver.resolve(id,targetType);
325N/A }
325N/A
325N/A//
325N/A//
325N/A// namespace binding maintainance
325N/A//
325N/A//
325N/A private String[] nsBind = new String[16];
325N/A private int nsLen=0;
325N/A
325N/A public void startPrefixMapping( String prefix, String uri ) {
325N/A if(nsBind.length==nsLen) {
325N/A // expand the buffer
325N/A String[] n = new String[nsLen*2];
325N/A System.arraycopy(nsBind,0,n,0,nsLen);
325N/A nsBind=n;
325N/A }
325N/A nsBind[nsLen++] = prefix;
325N/A nsBind[nsLen++] = uri;
325N/A }
325N/A public void endPrefixMapping( String prefix ) {
325N/A nsLen-=2;
325N/A }
325N/A private String resolveNamespacePrefix( String prefix ) {
325N/A if(prefix.equals("xml"))
325N/A return XMLConstants.XML_NS_URI;
325N/A
325N/A for( int i=nsLen-2; i>=0; i-=2 ) {
325N/A if(prefix.equals(nsBind[i]))
325N/A return nsBind[i+1];
325N/A }
325N/A
325N/A if(environmentNamespaceContext!=null)
325N/A // temporary workaround until Zephyr fixes 6337180
325N/A return environmentNamespaceContext.getNamespaceURI(prefix.intern());
325N/A
325N/A // by default, the default ns is bound to "".
325N/A // but allow environmentNamespaceContext to take precedence
325N/A if(prefix.equals(""))
325N/A return "";
325N/A
325N/A // unresolved. error.
325N/A return null;
325N/A }
325N/A
325N/A /**
325N/A * Returns a list of prefixes newly declared on the current element.
325N/A *
325N/A * @return
325N/A * A possible zero-length array of prefixes. The default prefix
325N/A * is represented by the empty string.
325N/A */
325N/A public String[] getNewlyDeclaredPrefixes() {
325N/A return getPrefixList( current.prev.numNsDecl );
325N/A }
325N/A
325N/A /**
325N/A * Returns a list of all in-scope prefixes.
325N/A *
325N/A * @return
325N/A * A possible zero-length array of prefixes. The default prefix
325N/A * is represented by the empty string.
325N/A */
325N/A public String[] getAllDeclaredPrefixes() {
325N/A return getPrefixList(0);
325N/A }
325N/A
325N/A private String[] getPrefixList( int startIndex ) {
325N/A int size = (current.numNsDecl - startIndex)/2;
325N/A String[] r = new String[size];
325N/A for( int i=0; i<r.length; i++ )
325N/A r[i] = nsBind[startIndex+i*2];
325N/A return r;
325N/A }
325N/A
325N/A // NamespaceContext2 implementation
325N/A //
325N/A public Iterator<String> getPrefixes(String uri) {
325N/A // TODO: could be implemented much faster
325N/A // wrap it into unmodifiable list so that the remove method
325N/A // will throw UnsupportedOperationException.
325N/A return Collections.unmodifiableList(
325N/A getAllPrefixesInList(uri)).iterator();
325N/A }
325N/A
325N/A private List<String> getAllPrefixesInList(String uri) {
325N/A List<String> a = new ArrayList<String>();
325N/A
325N/A if( uri==null )
325N/A throw new IllegalArgumentException();
325N/A if( uri.equals(XMLConstants.XML_NS_URI) ) {
325N/A a.add(XMLConstants.XML_NS_PREFIX);
325N/A return a;
325N/A }
325N/A if( uri.equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI) ) {
325N/A a.add(XMLConstants.XMLNS_ATTRIBUTE);
325N/A return a;
325N/A }
325N/A
325N/A for( int i=nsLen-2; i>=0; i-=2 )
325N/A if(uri.equals(nsBind[i+1]))
325N/A if( getNamespaceURI(nsBind[i]).equals(nsBind[i+1]) )
325N/A // make sure that this prefix is still effective.
325N/A a.add(nsBind[i]);
325N/A
325N/A return a;
325N/A }
325N/A
325N/A public String getPrefix(String uri) {
325N/A if( uri==null )
325N/A throw new IllegalArgumentException();
325N/A if( uri.equals(XMLConstants.XML_NS_URI) )
325N/A return XMLConstants.XML_NS_PREFIX;
325N/A if( uri.equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI) )
325N/A return XMLConstants.XMLNS_ATTRIBUTE;
325N/A
325N/A for( int i=nsLen-2; i>=0; i-=2 )
325N/A if(uri.equals(nsBind[i+1]))
325N/A if( getNamespaceURI(nsBind[i]).equals(nsBind[i+1]) )
325N/A // make sure that this prefix is still effective.
325N/A return nsBind[i];
325N/A
325N/A if(environmentNamespaceContext!=null)
325N/A return environmentNamespaceContext.getPrefix(uri);
325N/A
325N/A return null;
325N/A }
325N/A
325N/A public String getNamespaceURI(String prefix) {
325N/A if (prefix == null)
325N/A throw new IllegalArgumentException();
325N/A if (prefix.equals(XMLConstants.XMLNS_ATTRIBUTE))
325N/A return XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
325N/A
325N/A return resolveNamespacePrefix(prefix);
325N/A }
325N/A
325N/A//
325N/A//
325N/A//
325N/A// scope management
325N/A//
325N/A//
325N/A//
325N/A private Scope[] scopes = new Scope[16];
325N/A /**
325N/A * Points to the top of the scope stack (=size-1).
325N/A */
325N/A private int scopeTop=0;
325N/A
325N/A {
325N/A for( int i=0; i<scopes.length; i++ )
325N/A scopes[i] = new Scope(this);
325N/A }
325N/A
325N/A /**
325N/A * Starts a new packing scope.
325N/A *
325N/A * <p>
325N/A * This method allocates a specified number of fresh {@link Scope} objects.
325N/A * They can be accessed by the {@link #getScope} method until the corresponding
325N/A * {@link #endScope} method is invoked.
325N/A *
325N/A * <p>
325N/A * A new scope will mask the currently active scope. Only one frame of {@link Scope}s
325N/A * can be accessed at any given time.
325N/A *
325N/A * @param frameSize
325N/A * The # of slots to be allocated.
325N/A */
325N/A public void startScope(int frameSize) {
325N/A scopeTop += frameSize;
325N/A
325N/A // reallocation
325N/A if(scopeTop>=scopes.length) {
325N/A Scope[] s = new Scope[Math.max(scopeTop+1,scopes.length*2)];
325N/A System.arraycopy(scopes,0,s,0,scopes.length);
325N/A for( int i=scopes.length; i<s.length; i++ )
325N/A s[i] = new Scope(this);
325N/A scopes = s;
325N/A }
325N/A }
325N/A
325N/A /**
325N/A * Ends the current packing scope.
325N/A *
325N/A * <p>
325N/A * If any packing in progress will be finalized by this method.
325N/A *
325N/A * @param frameSize
325N/A * The same size that gets passed to the {@link #startScope(int)}
325N/A * method.
325N/A */
325N/A public void endScope(int frameSize) throws SAXException {
325N/A try {
325N/A for( ; frameSize>0; frameSize--, scopeTop-- )
325N/A scopes[scopeTop].finish();
325N/A } catch (AccessorException e) {
325N/A handleError(e);
325N/A
325N/A // the error might have left scopes in inconsistent state,
325N/A // so replace them by fresh ones
325N/A for( ; frameSize>0; frameSize-- )
325N/A scopes[scopeTop--] = new Scope(this);
325N/A }
325N/A }
325N/A
325N/A /**
325N/A * Gets the currently active {@link Scope}.
325N/A *
325N/A * @param offset
325N/A * a number between [0,frameSize)
325N/A *
325N/A * @return
325N/A * always a valid {@link Scope} object.
325N/A */
325N/A public Scope getScope(int offset) {
325N/A return scopes[scopeTop-offset];
325N/A }
325N/A
325N/A//
325N/A//
325N/A//
325N/A//
325N/A//
325N/A//
325N/A//
325N/A
325N/A private static final Loader DEFAULT_ROOT_LOADER = new DefaultRootLoader();
325N/A private static final Loader EXPECTED_TYPE_ROOT_LOADER = new ExpectedTypeRootLoader();
325N/A
325N/A /**
325N/A * Root loader that uses the tag name and possibly its @xsi:type
325N/A * to decide how to start unmarshalling.
325N/A */
325N/A private static final class DefaultRootLoader extends Loader implements Receiver {
325N/A /**
325N/A * Receives the root element and determines how to start
325N/A * unmarshalling.
325N/A */
325N/A @Override
325N/A public void childElement(UnmarshallingContext.State state, TagName ea) throws SAXException {
325N/A Loader loader = state.getContext().selectRootLoader(state,ea);
325N/A if(loader!=null) {
325N/A state.loader = loader;
325N/A state.receiver = this;
325N/A return;
325N/A }
325N/A
325N/A // the registry doesn't know about this element.
325N/A // try its xsi:type
325N/A JaxBeanInfo beanInfo = XsiTypeLoader.parseXsiType(state, ea, null);
325N/A if(beanInfo==null) {
325N/A // we don't even know its xsi:type
325N/A reportUnexpectedChildElement(ea,false);
325N/A return;
325N/A }
325N/A
325N/A state.loader = beanInfo.getLoader(null,false);
325N/A state.prev.backup = new JAXBElement<Object>(ea.createQName(),Object.class,null);
325N/A state.receiver = this;
325N/A }
325N/A
325N/A @Override
325N/A public Collection<QName> getExpectedChildElements() {
325N/A return getInstance().getJAXBContext().getValidRootNames();
325N/A }
325N/A
325N/A public void receive(State state, Object o) {
325N/A if(state.backup!=null) {
325N/A ((JAXBElement<Object>)state.backup).setValue(o);
325N/A o = state.backup;
325N/A }
325N/A if (state.nil) {
325N/A ((JAXBElement<Object>)o).setNil(true);
325N/A }
325N/A state.getContext().result = o;
325N/A }
325N/A }
325N/A
325N/A /**
325N/A * Root loader that uses {@link UnmarshallingContext#expectedType}
325N/A * to decide how to start unmarshalling.
325N/A */
325N/A private static final class ExpectedTypeRootLoader extends Loader implements Receiver {
325N/A /**
325N/A * Receives the root element and determines how to start
325N/A * unmarshalling.
325N/A */
325N/A @Override
325N/A public void childElement(UnmarshallingContext.State state, TagName ea) {
325N/A UnmarshallingContext context = state.getContext();
325N/A
325N/A // unmarshals the specified type
325N/A QName qn = new QName(ea.uri,ea.local);
325N/A state.prev.target = new JAXBElement(qn,context.expectedType.jaxbType,null,null);
325N/A state.receiver = this;
325N/A // this is bit wasteful, as in theory we should have each expectedType keep
325N/A // nillable version --- but that increases the combination from two to four,
325N/A // which adds the resident memory footprint. Since XsiNilLoader is small,
325N/A // I intentionally allocate a new instance freshly.
325N/A state.loader = new XsiNilLoader(context.expectedType.getLoader(null,true));
325N/A }
325N/A
325N/A public void receive(State state, Object o) {
325N/A JAXBElement e = (JAXBElement)state.target;
325N/A e.setValue(o);
325N/A state.getContext().recordOuterPeer(e);
325N/A state.getContext().result = e;
325N/A }
325N/A }
325N/A
325N/A//
325N/A// in-place unmarshalling related capabilities
325N/A//
325N/A /**
325N/A * Notifies the context about the inner peer of the current element.
325N/A *
325N/A * <p>
325N/A * If the unmarshalling is building the association, the context
325N/A * will use this information. Otherwise it will be just ignored.
325N/A */
325N/A public void recordInnerPeer(Object innerPeer) {
325N/A if(assoc!=null)
325N/A assoc.addInner(currentElement,innerPeer);
325N/A }
325N/A
325N/A /**
325N/A * Gets the inner peer JAXB object associated with the current element.
325N/A *
325N/A * @return
325N/A * null if the current element doesn't have an inner peer,
325N/A * or if we are not doing the in-place unmarshalling.
325N/A */
325N/A public Object getInnerPeer() {
325N/A if(assoc!=null && isInplaceMode)
325N/A return assoc.getInnerPeer(currentElement);
325N/A else
325N/A return null;
325N/A }
325N/A
325N/A /**
325N/A * Notifies the context about the outer peer of the current element.
325N/A *
325N/A * <p>
325N/A * If the unmarshalling is building the association, the context
325N/A * will use this information. Otherwise it will be just ignored.
325N/A */
325N/A public void recordOuterPeer(Object outerPeer) {
325N/A if(assoc!=null)
325N/A assoc.addOuter(currentElement,outerPeer);
325N/A }
325N/A
325N/A /**
325N/A * Gets the outer peer JAXB object associated with the current element.
325N/A *
325N/A * @return
325N/A * null if the current element doesn't have an inner peer,
325N/A * or if we are not doing the in-place unmarshalling.
325N/A */
325N/A public Object getOuterPeer() {
325N/A if(assoc!=null && isInplaceMode)
325N/A return assoc.getOuterPeer(currentElement);
325N/A else
325N/A return null;
325N/A }
325N/A
325N/A /**
325N/A * Gets the xmime:contentType value for the current object.
325N/A *
325N/A * @see JAXBContextImpl#getXMIMEContentType(Object)
325N/A */
325N/A public String getXMIMEContentType() {
325N/A /*
325N/A this won't work when the class is like
325N/A
325N/A class Foo {
325N/A @XmlValue Image img;
325N/A }
325N/A
325N/A because the target will return Foo, not the class enclosing Foo
325N/A which will have xmime:contentType
325N/A */
325N/A Object t = current.target;
325N/A if(t==null) return null;
325N/A return getJAXBContext().getXMIMEContentType(t);
325N/A }
325N/A
325N/A /**
325N/A * When called from within the realm of the unmarshaller, this method
325N/A * returns the current {@link UnmarshallingContext} in charge.
325N/A */
325N/A public static UnmarshallingContext getInstance() {
325N/A return (UnmarshallingContext) Coordinator._getInstance();
325N/A }
325N/A
325N/A /**
325N/A * Allows to access elements which are expected in current state.
325N/A * Useful for getting elements for current parent.
325N/A *
325N/A * @return
325N/A */
325N/A public Collection<QName> getCurrentExpectedElements() {
325N/A pushCoordinator();
325N/A try {
325N/A State s = getCurrentState();
325N/A Loader l = s.loader;
325N/A return (l != null) ? l.getExpectedChildElements() : null;
325N/A } finally {
325N/A popCoordinator();
325N/A }
325N/A }
325N/A
325N/A /**
325N/A * Allows to access attributes which are expected in current state.
325N/A * Useful for getting attributes for current parent.
325N/A *
325N/A * @return
325N/A */
325N/A public Collection<QName> getCurrentExpectedAttributes() {
325N/A pushCoordinator();
325N/A try {
325N/A State s = getCurrentState();
325N/A Loader l = s.loader;
325N/A return (l != null) ? l.getExpectedAttributes() : null;
325N/A } finally {
325N/A popCoordinator();
325N/A }
325N/A }
325N/A
325N/A /**
325N/A * Gets StructureLoader if used as loader.
325N/A * Useful when determining if element is mixed or not.
325N/A *
325N/A */
325N/A public StructureLoader getStructureLoader() {
325N/A if(current.loader instanceof StructureLoader)
325N/A return (StructureLoader)current.loader;
325N/A
325N/A return null;
325N/A }
325N/A
325N/A}