src/share/jaxws_classes/com/sun/xml/internal/bind/v2/runtime/unmarshaller/UnmarshallingContext.java

Thu, 31 Aug 2017 15:18:52 +0800

author
aoqi
date
Thu, 31 Aug 2017 15:18:52 +0800
changeset 637
9c07ef4934dd
parent 397
b99d7e355d4b
parent 0
373ffda63c9a
child 760
e530533619ec
permissions
-rw-r--r--

merge

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

mercurial