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