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

Sun, 31 Aug 2014 16:14:36 +0400

author
aefimov
date
Sun, 31 Aug 2014 16:14:36 +0400
changeset 707
31893650acaf
parent 650
121e938cb9c3
child 760
e530533619ec
permissions
-rw-r--r--

8036981: JAXB not preserving formatting for xsd:any Mixed content
Reviewed-by: lancea, mkos

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

mercurial