0001: /*
0002: * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
0003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
0004: *
0005: * This code is free software; you can redistribute it and/or modify it
0006: * under the terms of the GNU General Public License version 2 only, as
0007: * published by the Free Software Foundation. Sun designates this
0008: * particular file as subject to the "Classpath" exception as provided
0009: * by Sun in the LICENSE file that accompanied this code.
0010: *
0011: * This code is distributed in the hope that it will be useful, but WITHOUT
0012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
0013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
0014: * version 2 for more details (a copy is included in the LICENSE file that
0015: * accompanied this code).
0016: *
0017: * You should have received a copy of the GNU General Public License version
0018: * 2 along with this work; if not, write to the Free Software Foundation,
0019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
0020: *
0021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
0022: * CA 95054 USA or visit www.sun.com if you need additional information or
0023: * have any questions.
0024: */
0025:
0026: package com.sun.xml.internal.bind.v2.runtime.unmarshaller;
0027:
0028: import java.lang.reflect.InvocationTargetException;
0029: import java.lang.reflect.Method;
0030: import java.util.ArrayList;
0031: import java.util.Collection;
0032: import java.util.Collections;
0033: import java.util.HashMap;
0034: import java.util.Iterator;
0035: import java.util.List;
0036: import java.util.Map;
0037: import java.util.concurrent.Callable;
0038:
0039: import javax.xml.XMLConstants;
0040: import javax.xml.bind.JAXBElement;
0041: import javax.xml.bind.UnmarshalException;
0042: import javax.xml.bind.Unmarshaller;
0043: import javax.xml.bind.ValidationEvent;
0044: import javax.xml.bind.ValidationEventHandler;
0045: import javax.xml.bind.ValidationEventLocator;
0046: import javax.xml.bind.helpers.ValidationEventImpl;
0047: import javax.xml.bind.helpers.ValidationEventLocatorImpl;
0048: import javax.xml.namespace.NamespaceContext;
0049: import javax.xml.namespace.QName;
0050:
0051: import com.sun.istack.internal.NotNull;
0052: import com.sun.istack.internal.SAXParseException2;
0053: import com.sun.xml.internal.bind.IDResolver;
0054: import com.sun.xml.internal.bind.api.AccessorException;
0055: import com.sun.xml.internal.bind.unmarshaller.InfosetScanner;
0056: import com.sun.xml.internal.bind.v2.ClassFactory;
0057: import com.sun.xml.internal.bind.v2.runtime.AssociationMap;
0058: import com.sun.xml.internal.bind.v2.runtime.Coordinator;
0059: import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl;
0060: import com.sun.xml.internal.bind.v2.runtime.JaxBeanInfo;
0061:
0062: import org.xml.sax.ErrorHandler;
0063: import org.xml.sax.Locator;
0064: import org.xml.sax.SAXException;
0065: import org.xml.sax.helpers.LocatorImpl;
0066:
0067: /**
0068: * Center of the unmarshalling.
0069: *
0070: * <p>
0071: * This object is responsible for coordinating {@link Loader}s to
0072: * perform the whole unmarshalling.
0073: *
0074: * @author Kohsuke Kawaguchi
0075: */
0076: public final class UnmarshallingContext extends Coordinator implements
0077: NamespaceContext, ValidationEventHandler, ErrorHandler,
0078: XmlVisitor, XmlVisitor.TextPredictor {
0079:
0080: /**
0081: * Root state.
0082: */
0083: private final State root;
0084:
0085: /**
0086: * The currently active state.
0087: */
0088: private State current;
0089:
0090: private @NotNull
0091: LocatorEx locator = DUMMY_INSTANCE;
0092:
0093: /** Root object that is being unmarshalled. */
0094: private Object result;
0095:
0096: /**
0097: * If non-null, this unmarshaller will unmarshal {@code JAXBElement<EXPECTEDTYPE>}
0098: * regardless of the tag name, as opposed to deciding the root object by using
0099: * the tag name.
0100: *
0101: * The property has a package-level access, because we cannot copy this value
0102: * to {@link UnmarshallingContext} when it is created. The property
0103: * on {@link Unmarshaller} could be changed after the handler is created.
0104: */
0105: private JaxBeanInfo expectedType;
0106:
0107: /**
0108: * Handles ID/IDREF.
0109: */
0110: private IDResolver idResolver;
0111:
0112: /**
0113: * This flag is set to true at the startDocument event
0114: * and false at the endDocument event.
0115: *
0116: * Until the first document is unmarshalled, we don't
0117: * want to return an object. So this variable is initialized
0118: * to true.
0119: */
0120: private boolean isUnmarshalInProgress = true;
0121: private boolean aborted = false;
0122:
0123: public final UnmarshallerImpl parent;
0124:
0125: /**
0126: * If the unmarshaller is doing associative unmarshalling,
0127: * this field is initialized to non-null.
0128: */
0129: private final AssociationMap assoc;
0130:
0131: /**
0132: * Indicates whether we are doing in-place unmarshalling
0133: * or not.
0134: *
0135: * <p>
0136: * This flag is unused when {@link #assoc}==null.
0137: * If it's non-null, then <tt>true</tt> indicates
0138: * that we are doing in-place associative unmarshalling.
0139: * If <tt>false</tt>, then we are doing associative unmarshalling
0140: * without object reuse.
0141: */
0142: private boolean isInplaceMode;
0143:
0144: /**
0145: * This object is consulted to get the element object for
0146: * the current element event.
0147: *
0148: * This is used when we are building an association map.
0149: */
0150: private InfosetScanner scanner;
0151:
0152: private Object currentElement;
0153:
0154: /**
0155: * @see XmlVisitor#startDocument(LocatorEx, NamespaceContext)
0156: */
0157: private NamespaceContext environmentNamespaceContext;
0158:
0159: /**
0160: * State information for each element.
0161: */
0162: public final class State {
0163: /**
0164: * Loader that owns this element.
0165: */
0166: public Loader loader;
0167: /**
0168: * Once {@link #loader} is completed, this receiver
0169: * receives the result.
0170: */
0171: public Receiver receiver;
0172:
0173: public Intercepter intercepter;
0174:
0175: /**
0176: * Object being unmarshalled by this {@link #loader}.
0177: */
0178: public Object target;
0179:
0180: /**
0181: * Hack for making JAXBElement unmarshalling work.
0182: */
0183: public Object backup;
0184:
0185: /**
0186: * Number of {@link UnmarshallingContext#nsBind}s declared thus far.
0187: * (The value of {@link UnmarshallingContext#nsLen} when this state is pushed.
0188: */
0189: private int numNsDecl;
0190:
0191: /**
0192: * If this element has an element default value.
0193: *
0194: * This should be set by either a parent {@link Loader} when
0195: * {@link Loader#childElement(State, TagName)} is called
0196: * or by a child {@link Loader} when
0197: * {@link Loader#startElement(State, TagName)} is called.
0198: */
0199: public String elementDefaultValue;
0200:
0201: /**
0202: * {@link State} for the parent element
0203: *
0204: * {@link State} objects form a doubly linked list.
0205: */
0206: public final State prev;
0207: private State next;
0208:
0209: /**
0210: * Gets the context.
0211: */
0212: public UnmarshallingContext getContext() {
0213: return UnmarshallingContext.this ;
0214: }
0215:
0216: private State(State prev) {
0217: this .prev = prev;
0218: if (prev != null)
0219: prev.next = this ;
0220: }
0221:
0222: private void push() {
0223: if (next == null)
0224: allocateMoreStates();
0225: State n = next;
0226: n.numNsDecl = nsLen;
0227: current = n;
0228: }
0229:
0230: private void pop() {
0231: assert prev != null;
0232: loader = null;
0233: receiver = null;
0234: intercepter = null;
0235: elementDefaultValue = null;
0236: target = null;
0237: current = prev;
0238: }
0239: }
0240:
0241: /**
0242: * Stub to the user-specified factory method.
0243: */
0244: private static class Factory {
0245: private final Object factorInstance;
0246: private final Method method;
0247:
0248: public Factory(Object factorInstance, Method method) {
0249: this .factorInstance = factorInstance;
0250: this .method = method;
0251: }
0252:
0253: public Object createInstance() throws SAXException {
0254: try {
0255: return method.invoke(factorInstance);
0256: } catch (IllegalAccessException e) {
0257: getInstance().handleError(e, false);
0258: } catch (InvocationTargetException e) {
0259: getInstance().handleError(e, false);
0260: }
0261: return null; // can never be executed
0262: }
0263: }
0264:
0265: /**
0266: * Creates a new unmarshaller.
0267: *
0268: * @param assoc
0269: * Must be both non-null when the unmarshaller does the
0270: * in-place unmarshalling. Otherwise must be both null.
0271: */
0272: public UnmarshallingContext(UnmarshallerImpl _parent,
0273: AssociationMap assoc) {
0274: this .parent = _parent;
0275: this .assoc = assoc;
0276: this .root = this .current = new State(null);
0277: allocateMoreStates();
0278: }
0279:
0280: public void reset(InfosetScanner scanner, boolean isInplaceMode,
0281: JaxBeanInfo expectedType, IDResolver idResolver) {
0282: this .scanner = scanner;
0283: this .isInplaceMode = isInplaceMode;
0284: this .expectedType = expectedType;
0285: this .idResolver = idResolver;
0286: }
0287:
0288: public JAXBContextImpl getJAXBContext() {
0289: return parent.context;
0290: }
0291:
0292: public State getCurrentState() {
0293: return current;
0294: }
0295:
0296: /**
0297: * Allocates a few more {@link State}s.
0298: *
0299: * Allocating multiple {@link State}s at once allows those objects
0300: * to be allocated near each other, which reduces the working set
0301: * of CPU. It improves the chance the relevant data is in the cache.
0302: */
0303: private void allocateMoreStates() {
0304: // this method should be used only when we run out of a state.
0305: assert current.next == null;
0306:
0307: State s = current;
0308: for (int i = 0; i < 8; i++)
0309: s = new State(s);
0310: }
0311:
0312: /**
0313: * User-specified factory methods.
0314: */
0315: private final Map<Class, Factory> factories = new HashMap<Class, Factory>();
0316:
0317: public void setFactories(Object factoryInstances) {
0318: factories.clear();
0319: if (factoryInstances == null) {
0320: return;
0321: }
0322: if (factoryInstances instanceof Object[]) {
0323: for (Object factory : (Object[]) factoryInstances) {
0324: // look for all the public methods inlcuding derived ones
0325: addFactory(factory);
0326: }
0327: } else {
0328: addFactory(factoryInstances);
0329: }
0330: }
0331:
0332: private void addFactory(Object factory) {
0333: for (Method m : factory.getClass().getMethods()) {
0334: // look for methods whose signature is T createXXX()
0335: if (!m.getName().startsWith("create"))
0336: continue;
0337: if (m.getParameterTypes().length > 0)
0338: continue;
0339:
0340: Class type = m.getReturnType();
0341:
0342: factories.put(type, new Factory(factory, m));
0343: }
0344: }
0345:
0346: public void startDocument(LocatorEx locator,
0347: NamespaceContext nsContext) throws SAXException {
0348: this .locator = locator;
0349: this .environmentNamespaceContext = nsContext;
0350: // reset the object
0351: result = null;
0352: current = root;
0353:
0354: patchersLen = 0;
0355: aborted = false;
0356: isUnmarshalInProgress = true;
0357: nsLen = 0;
0358:
0359: startPrefixMapping("", ""); // by default, the default ns is bound to "".
0360:
0361: setThreadAffinity();
0362:
0363: if (expectedType != null)
0364: root.loader = EXPECTED_TYPE_ROOT_LOADER;
0365: else
0366: root.loader = DEFAULT_ROOT_LOADER;
0367:
0368: idResolver.startDocument(this );
0369: }
0370:
0371: public void startElement(TagName tagName) throws SAXException {
0372: pushCoordinator();
0373: try {
0374: _startElement(tagName);
0375: } finally {
0376: popCoordinator();
0377: }
0378: }
0379:
0380: private void _startElement(TagName tagName) throws SAXException {
0381:
0382: // remember the current element if we are interested in it.
0383: // because the inner peer might not be found while we consume
0384: // the enter element token, we need to keep this information
0385: // longer than this callback. That's why we assign it to a field.
0386: if (assoc != null)
0387: currentElement = scanner.getCurrentElement();
0388:
0389: Loader h = current.loader;
0390: current.push();
0391:
0392: // tell the parent about the new child
0393: h.childElement(current, tagName);
0394: assert current.loader != null; // the childElement should register this
0395: // and tell the new child that you are activated
0396: current.loader.startElement(current, tagName);
0397: }
0398:
0399: public void text(CharSequence pcdata) throws SAXException {
0400: State cur = current;
0401: pushCoordinator();
0402: try {
0403: if (cur.elementDefaultValue != null) {
0404: if (pcdata.length() == 0) {
0405: // send the default value into the unmarshaller instead
0406: pcdata = cur.elementDefaultValue;
0407: }
0408: }
0409: cur.loader.text(cur, pcdata);
0410: } finally {
0411: popCoordinator();
0412: }
0413: }
0414:
0415: public final void endElement(TagName tagName) throws SAXException {
0416: pushCoordinator();
0417: try {
0418: State child = current;
0419:
0420: // tell the child that your time is up
0421: child.loader.leaveElement(child, tagName);
0422:
0423: // child.pop will erase them so store them now
0424: Object target = child.target;
0425: Receiver recv = child.receiver;
0426: Intercepter intercepter = child.intercepter;
0427: child.pop();
0428:
0429: // then let the parent know
0430: if (intercepter != null)
0431: target = intercepter.intercept(current, target);
0432: if (recv != null)
0433: recv.receive(current, target);
0434: } finally {
0435: popCoordinator();
0436: }
0437: }
0438:
0439: public void endDocument() throws SAXException {
0440: runPatchers();
0441: idResolver.endDocument();
0442:
0443: isUnmarshalInProgress = false;
0444: currentElement = null;
0445: locator = DUMMY_INSTANCE;
0446: environmentNamespaceContext = null;
0447:
0448: // at the successful completion, scope must be all closed
0449: assert root == current;
0450:
0451: resetThreadAffinity();
0452: }
0453:
0454: /**
0455: * You should be always calling this through {@link TextPredictor}.
0456: */
0457: @Deprecated
0458: public boolean expectText() {
0459: return current.loader.expectText;
0460: }
0461:
0462: /**
0463: * You should be always getting {@link TextPredictor} from {@link XmlVisitor}.
0464: */
0465: @Deprecated
0466: public TextPredictor getPredictor() {
0467: return this ;
0468: }
0469:
0470: public UnmarshallingContext getContext() {
0471: return this ;
0472: }
0473:
0474: /**
0475: * Gets the result of the unmarshalling
0476: */
0477: public Object getResult() throws UnmarshalException {
0478: if (isUnmarshalInProgress)
0479: throw new IllegalStateException();
0480:
0481: if (!aborted)
0482: return result;
0483:
0484: // there was an error.
0485: throw new UnmarshalException((String) null);
0486: }
0487:
0488: /**
0489: * Creates a new instance of the specified class.
0490: * In the unmarshaller, we need to check the user-specified factory class.
0491: */
0492: public Object createInstance(Class<?> clazz) throws SAXException {
0493: if (!factories.isEmpty()) {
0494: Factory factory = factories.get(clazz);
0495: if (factory != null)
0496: return factory.createInstance();
0497: }
0498: return ClassFactory.create(clazz);
0499: }
0500:
0501: /**
0502: * Creates a new instance of the specified class.
0503: * In the unmarshaller, we need to check the user-specified factory class.
0504: */
0505: public Object createInstance(JaxBeanInfo beanInfo)
0506: throws SAXException {
0507: if (!factories.isEmpty()) {
0508: Factory factory = factories.get(beanInfo.jaxbType);
0509: if (factory != null)
0510: return factory.createInstance();
0511: }
0512: try {
0513: return beanInfo.createInstance(this );
0514: } catch (IllegalAccessException e) {
0515: Loader.reportError("Unable to create an instance of "
0516: + beanInfo.jaxbType.getName(), e, false);
0517: } catch (InvocationTargetException e) {
0518: Loader.reportError("Unable to create an instance of "
0519: + beanInfo.jaxbType.getName(), e, false);
0520: } catch (InstantiationException e) {
0521: Loader.reportError("Unable to create an instance of "
0522: + beanInfo.jaxbType.getName(), e, false);
0523: }
0524: return null; // can never be here
0525: }
0526:
0527: //
0528: //
0529: // error handling
0530: //
0531: //
0532:
0533: /**
0534: * Reports an error to the user, and asks if s/he wants
0535: * to recover. If the canRecover flag is false, regardless
0536: * of the client instruction, an exception will be thrown.
0537: *
0538: * Only if the flag is true and the user wants to recover from an error,
0539: * the method returns normally.
0540: *
0541: * The thrown exception will be catched by the unmarshaller.
0542: */
0543: public void handleEvent(ValidationEvent event, boolean canRecover)
0544: throws SAXException {
0545: ValidationEventHandler eventHandler = parent.getEventHandler();
0546:
0547: boolean recover = eventHandler.handleEvent(event);
0548:
0549: // if the handler says "abort", we will not return the object
0550: // from the unmarshaller.getResult()
0551: if (!recover)
0552: aborted = true;
0553:
0554: if (!canRecover || !recover)
0555: throw new SAXParseException2(event.getMessage(), locator,
0556: new UnmarshalException(event.getMessage(), event
0557: .getLinkedException()));
0558: }
0559:
0560: public boolean handleEvent(ValidationEvent event) {
0561: try {
0562: // if the handler says "abort", we will not return the object.
0563: boolean recover = parent.getEventHandler().handleEvent(
0564: event);
0565: if (!recover)
0566: aborted = true;
0567: return recover;
0568: } catch (RuntimeException re) {
0569: // if client event handler causes a runtime exception, then we
0570: // have to return false.
0571: return false;
0572: }
0573: }
0574:
0575: /**
0576: * Reports an exception found during the unmarshalling to the user.
0577: * This method is a convenience method that calls into
0578: * {@link #handleEvent(ValidationEvent, boolean)}
0579: */
0580: public void handleError(Exception e) throws SAXException {
0581: handleError(e, true);
0582: }
0583:
0584: public void handleError(Exception e, boolean canRecover)
0585: throws SAXException {
0586: handleEvent(new ValidationEventImpl(ValidationEvent.ERROR, e
0587: .getMessage(), locator.getLocation(), e), canRecover);
0588: }
0589:
0590: public void handleError(String msg) {
0591: handleEvent(new ValidationEventImpl(ValidationEvent.ERROR, msg,
0592: locator.getLocation()));
0593: }
0594:
0595: protected ValidationEventLocator getLocation() {
0596: return locator.getLocation();
0597: }
0598:
0599: /**
0600: * Gets the current source location information in SAX {@link Locator}.
0601: * <p>
0602: * Sometimes the unmarshaller works against a different kind of XML source,
0603: * making this information meaningless.
0604: */
0605: public LocatorEx getLocator() {
0606: return locator;
0607: }
0608:
0609: /**
0610: * Called when there's no corresponding ID value.
0611: */
0612: public void errorUnresolvedIDREF(Object bean, String idref)
0613: throws SAXException {
0614: handleEvent(new ValidationEventImpl(ValidationEvent.ERROR,
0615: Messages.UNRESOLVED_IDREF.format(idref),
0616: new ValidationEventLocatorImpl(bean)), true);
0617: }
0618:
0619: //
0620: //
0621: // ID/IDREF related code
0622: //
0623: //
0624: /**
0625: * Submitted patchers in the order they've submitted.
0626: * Many XML vocabulary doesn't use ID/IDREF at all, so we
0627: * initialize it with null.
0628: */
0629: private Patcher[] patchers = null;
0630: private int patchersLen = 0;
0631:
0632: /**
0633: * Adds a job that will be executed at the last of the unmarshalling.
0634: * This method is used to support ID/IDREF feature, but it can be used
0635: * for other purposes as well.
0636: *
0637: * @param job
0638: * The run method of this object is called.
0639: */
0640: public void addPatcher(Patcher job) {
0641: // re-allocate buffer if necessary
0642: if (patchers == null)
0643: patchers = new Patcher[32];
0644: if (patchers.length == patchersLen) {
0645: Patcher[] buf = new Patcher[patchersLen * 2];
0646: System.arraycopy(patchers, 0, buf, 0, patchersLen);
0647: patchers = buf;
0648: }
0649: patchers[patchersLen++] = job;
0650: }
0651:
0652: /** Executes all the patchers. */
0653: private void runPatchers() throws SAXException {
0654: if (patchers != null) {
0655: for (int i = 0; i < patchersLen; i++) {
0656: patchers[i].run();
0657: patchers[i] = null; // free memory
0658: }
0659: }
0660: }
0661:
0662: /**
0663: * Adds the object which is currently being unmarshalled
0664: * to the ID table.
0665: *
0666: * @return
0667: * Returns the value passed as the parameter.
0668: * This is a hack, but this makes it easier for ID
0669: * transducer to do its job.
0670: */
0671: // TODO: what shall we do if the ID is already declared?
0672: //
0673: // throwing an exception is one way. Overwriting the previous one
0674: // is another way. The latter allows us to process invalid documents,
0675: // while the former makes it impossible to handle them.
0676: //
0677: // I prefer to be flexible in terms of invalid document handling,
0678: // so chose not to throw an exception.
0679: //
0680: // I believe this is an implementation choice, not the spec issue.
0681: // -kk
0682: public String addToIdTable(String id) throws SAXException {
0683: // Hmm...
0684: // in cases such as when ID is used as an attribute, or as @XmlValue
0685: // the target wilil be current.target.
0686: // but in some other cases, such as when ID is used as a child element
0687: // or a value of JAXBElement, it's current.prev.target.
0688: // I don't know if this detection logic is complete
0689: Object o = current.target;
0690: if (o == null)
0691: o = current.prev.target;
0692: idResolver.bind(id, o);
0693: return id;
0694: }
0695:
0696: /**
0697: * Looks up the ID table and gets associated object.
0698: *
0699: * <p>
0700: * The exception thrown from {@link Callable#call()} means the unmarshaller should abort
0701: * right away.
0702: *
0703: * @see IDResolver#resolve(String, Class)
0704: */
0705: public Callable getObjectFromId(String id, Class targetType)
0706: throws SAXException {
0707: return idResolver.resolve(id, targetType);
0708: }
0709:
0710: //
0711: //
0712: // namespace binding maintainance
0713: //
0714: //
0715: private String[] nsBind = new String[16];
0716: private int nsLen = 0;
0717:
0718: public void startPrefixMapping(String prefix, String uri) {
0719: if (nsBind.length == nsLen) {
0720: // expand the buffer
0721: String[] n = new String[nsLen * 2];
0722: System.arraycopy(nsBind, 0, n, 0, nsLen);
0723: nsBind = n;
0724: }
0725: nsBind[nsLen++] = prefix;
0726: nsBind[nsLen++] = uri;
0727: }
0728:
0729: public void endPrefixMapping(String prefix) {
0730: nsLen -= 2;
0731: }
0732:
0733: private String resolveNamespacePrefix(String prefix) {
0734: if (prefix.equals("xml"))
0735: return "http://www.w3.org/XML/1998/namespace";
0736:
0737: for (int i = nsLen - 2; i >= 0; i -= 2) {
0738: if (prefix.equals(nsBind[i]))
0739: return nsBind[i + 1];
0740: }
0741:
0742: if (environmentNamespaceContext != null)
0743: // temporary workaround until Zephyr fixes 6337180
0744: return environmentNamespaceContext.getNamespaceURI(prefix
0745: .intern());
0746:
0747: return null;
0748: }
0749:
0750: /**
0751: * Returns a list of prefixes newly declared on the current element.
0752: *
0753: * @return
0754: * A possible zero-length array of prefixes. The default prefix
0755: * is represented by the empty string.
0756: */
0757: public String[] getNewlyDeclaredPrefixes() {
0758: return getPrefixList(current.prev.numNsDecl);
0759: }
0760:
0761: /**
0762: * Returns a list of all in-scope prefixes.
0763: *
0764: * @return
0765: * A possible zero-length array of prefixes. The default prefix
0766: * is represented by the empty string.
0767: */
0768: public String[] getAllDeclaredPrefixes() {
0769: return getPrefixList(2); // skip the default ""->"" mapping
0770: }
0771:
0772: private String[] getPrefixList(int startIndex) {
0773: int size = (current.numNsDecl - startIndex) / 2;
0774: String[] r = new String[size];
0775: for (int i = 0; i < r.length; i++)
0776: r[i] = nsBind[startIndex + i * 2];
0777: return r;
0778: }
0779:
0780: //
0781: // NamespaceContext2 implementation
0782: //
0783: public Iterator<String> getPrefixes(String uri) {
0784: // TODO: could be implemented much faster
0785: // wrap it into unmodifiable list so that the remove method
0786: // will throw UnsupportedOperationException.
0787: return Collections.unmodifiableList(getAllPrefixesInList(uri))
0788: .iterator();
0789: }
0790:
0791: private List<String> getAllPrefixesInList(String uri) {
0792: List<String> a = new ArrayList<String>();
0793:
0794: if (uri == null)
0795: throw new IllegalArgumentException();
0796: if (uri.equals(XMLConstants.XML_NS_URI)) {
0797: a.add(XMLConstants.XML_NS_PREFIX);
0798: return a;
0799: }
0800: if (uri.equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI)) {
0801: a.add(XMLConstants.XMLNS_ATTRIBUTE);
0802: return a;
0803: }
0804:
0805: for (int i = nsLen - 2; i >= 0; i -= 2)
0806: if (uri.equals(nsBind[i + 1]))
0807: if (getNamespaceURI(nsBind[i]).equals(nsBind[i + 1]))
0808: // make sure that this prefix is still effective.
0809: a.add(nsBind[i]);
0810:
0811: return a;
0812: }
0813:
0814: public String getPrefix(String uri) {
0815: if (uri == null)
0816: throw new IllegalArgumentException();
0817: if (uri.equals(XMLConstants.XML_NS_URI))
0818: return XMLConstants.XML_NS_PREFIX;
0819: if (uri.equals(XMLConstants.XMLNS_ATTRIBUTE_NS_URI))
0820: return XMLConstants.XMLNS_ATTRIBUTE;
0821:
0822: for (int i = nsLen - 2; i >= 0; i -= 2)
0823: if (uri.equals(nsBind[i + 1]))
0824: if (getNamespaceURI(nsBind[i]).equals(nsBind[i + 1]))
0825: // make sure that this prefix is still effective.
0826: return nsBind[i];
0827:
0828: if (environmentNamespaceContext != null)
0829: return environmentNamespaceContext.getPrefix(uri);
0830:
0831: return null;
0832: }
0833:
0834: public String getNamespaceURI(String prefix) {
0835: if (prefix == null)
0836: throw new IllegalArgumentException();
0837: if (prefix.equals(XMLConstants.XMLNS_ATTRIBUTE))
0838: return XMLConstants.XMLNS_ATTRIBUTE_NS_URI;
0839:
0840: return resolveNamespacePrefix(prefix);
0841: }
0842:
0843: //
0844: //
0845: //
0846: // scope management
0847: //
0848: //
0849: //
0850: private Scope[] scopes = new Scope[16];
0851: /**
0852: * Points to the top of the scope stack (=size-1).
0853: */
0854: private int scopeTop = 0;
0855:
0856: {
0857: for (int i = 0; i < scopes.length; i++)
0858: scopes[i] = new Scope(this );
0859: }
0860:
0861: /**
0862: * Starts a new packing scope.
0863: *
0864: * <p>
0865: * This method allocates a specified number of fresh {@link Scope} objects.
0866: * They can be accessed by the {@link #getScope} method until the corresponding
0867: * {@link #endScope} method is invoked.
0868: *
0869: * <p>
0870: * A new scope will mask the currently active scope. Only one frame of {@link Scope}s
0871: * can be accessed at any given time.
0872: *
0873: * @param frameSize
0874: * The # of slots to be allocated.
0875: */
0876: public void startScope(int frameSize) {
0877: scopeTop += frameSize;
0878:
0879: // reallocation
0880: if (scopeTop >= scopes.length) {
0881: Scope[] s = new Scope[Math.max(scopeTop + 1,
0882: scopes.length * 2)];
0883: System.arraycopy(scopes, 0, s, 0, scopes.length);
0884: for (int i = scopes.length; i < s.length; i++)
0885: s[i] = new Scope(this );
0886: scopes = s;
0887: }
0888: }
0889:
0890: /**
0891: * Ends the current packing scope.
0892: *
0893: * <p>
0894: * If any packing in progress will be finalized by this method.
0895: *
0896: * @param frameSize
0897: * The same size that gets passed to the {@link #startScope(int)}
0898: * method.
0899: */
0900: public void endScope(int frameSize) throws SAXException {
0901: try {
0902: for (; frameSize > 0; frameSize--)
0903: scopes[scopeTop--].finish();
0904: } catch (AccessorException e) {
0905: handleError(e);
0906: }
0907: }
0908:
0909: /**
0910: * Gets the currently active {@link Scope}.
0911: *
0912: * @param offset
0913: * a number between [0,frameSize)
0914: *
0915: * @return
0916: * always a valid {@link Scope} object.
0917: */
0918: public Scope getScope(int offset) {
0919: return scopes[scopeTop - offset];
0920: }
0921:
0922: //
0923: //
0924: //
0925: //
0926: //
0927: //
0928: //
0929:
0930: private static final Loader DEFAULT_ROOT_LOADER = new DefaultRootLoader();
0931: private static final Loader EXPECTED_TYPE_ROOT_LOADER = new ExpectedTypeRootLoader();
0932:
0933: /**
0934: * Root loader that uses the tag name and possibly its @xsi:type
0935: * to decide how to start unmarshalling.
0936: */
0937: private static final class DefaultRootLoader extends Loader
0938: implements Receiver {
0939: /**
0940: * Receives the root element and determines how to start
0941: * unmarshalling.
0942: */
0943: public void childElement(UnmarshallingContext.State state,
0944: TagName ea) throws SAXException {
0945: JAXBContextImpl jaxbContext = state.getContext()
0946: .getJAXBContext();
0947:
0948: Loader loader = jaxbContext.selectRootLoader(state, ea);
0949: if (loader != null) {
0950: state.loader = loader;
0951: state.receiver = this ;
0952: return;
0953: }
0954:
0955: // the registry doesn't know about this element.
0956: // try its xsi:type
0957: JaxBeanInfo beanInfo = XsiTypeLoader
0958: .parseXsiType(state, ea);
0959: if (beanInfo == null) {
0960: // we don't even know its xsi:type
0961: reportUnexpectedChildElement(ea, false);
0962: return;
0963: }
0964:
0965: state.loader = beanInfo.getLoader(null, false);
0966: state.prev.backup = new JAXBElement<Object>(ea
0967: .createQName(), Object.class, null);
0968: state.receiver = this ;
0969: }
0970:
0971: @Override
0972: public Collection<QName> getExpectedChildElements() {
0973: return getInstance().getJAXBContext().getValidRootNames();
0974: }
0975:
0976: public void receive(State state, Object o) {
0977: if (state.backup != null) {
0978: ((JAXBElement<Object>) state.backup).setValue(o);
0979: o = state.backup;
0980: }
0981: state.getContext().result = o;
0982: }
0983: }
0984:
0985: /**
0986: * Root loader that uses {@link UnmarshallingContext#expectedType}
0987: * to decide how to start unmarshalling.
0988: */
0989: private static final class ExpectedTypeRootLoader extends Loader
0990: implements Receiver {
0991: /**
0992: * Receives the root element and determines how to start
0993: * unmarshalling.
0994: */
0995: public void childElement(UnmarshallingContext.State state,
0996: TagName ea) {
0997: UnmarshallingContext context = state.getContext();
0998:
0999: // unmarshals the specified type
1000: QName qn = new QName(ea.uri, ea.local);
1001: state.prev.target = new JAXBElement(qn,
1002: context.expectedType.jaxbType, null, null);
1003: state.receiver = this ;
1004: // this is bit wasteful, as in theory we should have each expectedType keep
1005: // nillable version --- but that increases the combination from two to four,
1006: // which adds the resident memory footprint. Since XsiNilLoader is small,
1007: // I intentionally allocate a new instance freshly.
1008: state.loader = new XsiNilLoader(context.expectedType
1009: .getLoader(null, true));
1010: }
1011:
1012: public void receive(State state, Object o) {
1013: JAXBElement e = (JAXBElement) state.target;
1014: e.setValue(o);
1015: state.getContext().recordOuterPeer(e);
1016: state.getContext().result = e;
1017: }
1018: }
1019:
1020: //
1021: // in-place unmarshalling related capabilities
1022: //
1023: /**
1024: * Notifies the context about the inner peer of the current element.
1025: *
1026: * <p>
1027: * If the unmarshalling is building the association, the context
1028: * will use this information. Otherwise it will be just ignored.
1029: */
1030: public void recordInnerPeer(Object innerPeer) {
1031: if (assoc != null)
1032: assoc.addInner(currentElement, innerPeer);
1033: }
1034:
1035: /**
1036: * Gets the inner peer JAXB object associated with the current element.
1037: *
1038: * @return
1039: * null if the current element doesn't have an inner peer,
1040: * or if we are not doing the in-place unmarshalling.
1041: */
1042: public Object getInnerPeer() {
1043: if (assoc != null && isInplaceMode)
1044: return assoc.getInnerPeer(currentElement);
1045: else
1046: return null;
1047: }
1048:
1049: /**
1050: * Notifies the context about the outer peer of the current element.
1051: *
1052: * <p>
1053: * If the unmarshalling is building the association, the context
1054: * will use this information. Otherwise it will be just ignored.
1055: */
1056: public void recordOuterPeer(Object outerPeer) {
1057: if (assoc != null)
1058: assoc.addOuter(currentElement, outerPeer);
1059: }
1060:
1061: /**
1062: * Gets the outer peer JAXB object associated with the current element.
1063: *
1064: * @return
1065: * null if the current element doesn't have an inner peer,
1066: * or if we are not doing the in-place unmarshalling.
1067: */
1068: public Object getOuterPeer() {
1069: if (assoc != null && isInplaceMode)
1070: return assoc.getOuterPeer(currentElement);
1071: else
1072: return null;
1073: }
1074:
1075: /**
1076: * Gets the xmime:contentType value for the current object.
1077: *
1078: * @see JAXBContextImpl#getXMIMEContentType(Object)
1079: */
1080: public String getXMIMEContentType() {
1081: /*
1082: this won't work when the class is like
1083:
1084: class Foo {
1085: @XmlValue Image img;
1086: }
1087:
1088: because the target will return Foo, not the class enclosing Foo
1089: which will have xmime:contentType
1090: */
1091: Object t = current.target;
1092: if (t == null)
1093: return null;
1094: return getJAXBContext().getXMIMEContentType(t);
1095: }
1096:
1097: /**
1098: * When called from within the realm of the unmarshaller, this method
1099: * returns the current {@link UnmarshallingContext} in charge.
1100: */
1101: public static UnmarshallingContext getInstance() {
1102: return (UnmarshallingContext) Coordinator._getInstance();
1103: }
1104:
1105: private static final LocatorEx DUMMY_INSTANCE;
1106:
1107: static {
1108: LocatorImpl loc = new LocatorImpl();
1109: loc.setPublicId(null);
1110: loc.setSystemId(null);
1111: loc.setLineNumber(-1);
1112: loc.setColumnNumber(-1);
1113: DUMMY_INSTANCE = new LocatorExWrapper(loc);
1114: }
1115: }
|