aoqi@0: /* aoqi@0: * Copyright (c) 1997, 2011, Oracle and/or its affiliates. All rights reserved. aoqi@0: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. aoqi@0: * aoqi@0: * This code is free software; you can redistribute it and/or modify it aoqi@0: * under the terms of the GNU General Public License version 2 only, as aoqi@0: * published by the Free Software Foundation. Oracle designates this aoqi@0: * particular file as subject to the "Classpath" exception as provided aoqi@0: * by Oracle in the LICENSE file that accompanied this code. aoqi@0: * aoqi@0: * This code is distributed in the hope that it will be useful, but WITHOUT aoqi@0: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or aoqi@0: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License aoqi@0: * version 2 for more details (a copy is included in the LICENSE file that aoqi@0: * accompanied this code). aoqi@0: * aoqi@0: * You should have received a copy of the GNU General Public License version aoqi@0: * 2 along with this work; if not, write to the Free Software Foundation, aoqi@0: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. aoqi@0: * aoqi@0: * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA aoqi@0: * or visit www.oracle.com if you need additional information or have any aoqi@0: * questions. aoqi@0: */ aoqi@0: aoqi@0: package com.sun.xml.internal.bind.v2.runtime.unmarshaller; aoqi@0: aoqi@0: import java.util.Collection; aoqi@0: import java.util.HashMap; aoqi@0: import java.util.Map; aoqi@0: aoqi@0: import javax.xml.namespace.QName; aoqi@0: aoqi@0: import com.sun.xml.internal.bind.api.AccessorException; aoqi@0: import com.sun.xml.internal.bind.v2.WellKnownNamespace; aoqi@0: import com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl; aoqi@0: import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl; aoqi@0: import com.sun.xml.internal.bind.v2.runtime.JaxBeanInfo; aoqi@0: import com.sun.xml.internal.bind.v2.runtime.property.AttributeProperty; aoqi@0: import com.sun.xml.internal.bind.v2.runtime.property.Property; aoqi@0: import com.sun.xml.internal.bind.v2.runtime.property.StructureLoaderBuilder; aoqi@0: import com.sun.xml.internal.bind.v2.runtime.property.UnmarshallerChain; aoqi@0: import com.sun.xml.internal.bind.v2.runtime.reflect.Accessor; aoqi@0: import com.sun.xml.internal.bind.v2.runtime.reflect.TransducedAccessor; aoqi@0: import com.sun.xml.internal.bind.v2.util.QNameMap; aoqi@0: aoqi@0: import java.util.Iterator; aoqi@0: import org.xml.sax.Attributes; aoqi@0: import org.xml.sax.SAXException; aoqi@0: aoqi@0: /** aoqi@0: * Loads children of an element. aoqi@0: * aoqi@0: *

aoqi@0: * This loader works with a single {@link JaxBeanInfo} and handles aoqi@0: * attributes, child elements, or child text. aoqi@0: * aoqi@0: * @author Kohsuke Kawaguchi aoqi@0: */ aoqi@0: public final class StructureLoader extends Loader { aoqi@0: /** aoqi@0: * This map statically stores information of the aoqi@0: * unmarshaller loader and can be used while unmarshalling aoqi@0: * Since creating new QNames is expensive use this optimized aoqi@0: * version of the map aoqi@0: */ aoqi@0: private final QNameMap childUnmarshallers = new QNameMap(); aoqi@0: aoqi@0: /** aoqi@0: * Loader that processes elements that didn't match anf of the {@link #childUnmarshallers}. aoqi@0: * Can be null. aoqi@0: */ aoqi@0: private /*final*/ ChildLoader catchAll; aoqi@0: aoqi@0: /** aoqi@0: * If we have a loader for processing text. Otherwise null. aoqi@0: */ aoqi@0: private /*final*/ ChildLoader textHandler; aoqi@0: aoqi@0: /** aoqi@0: * Unmarshallers for attribute values. aoqi@0: * May be null if no attribute is expected and {@link #attCatchAll}==null. aoqi@0: */ aoqi@0: private /*final*/ QNameMap attUnmarshallers; aoqi@0: aoqi@0: /** aoqi@0: * This will receive all the attributes aoqi@0: * that were not processed. Never be null. aoqi@0: */ aoqi@0: private /*final*/ Accessor> attCatchAll; aoqi@0: aoqi@0: private final JaxBeanInfo beanInfo; aoqi@0: aoqi@0: /** aoqi@0: * The number of scopes this dispatcher needs to keep active. aoqi@0: */ aoqi@0: private /*final*/ int frameSize; aoqi@0: aoqi@0: // this class is potentially useful for general audience, not just for ClassBeanInfoImpl, aoqi@0: // but since right now that is the only user, we make the construction code very specific aoqi@0: // to ClassBeanInfoImpl. See rev.1.5 of this file for the original general purpose definition. aoqi@0: public StructureLoader(ClassBeanInfoImpl beanInfo) { aoqi@0: super(true); aoqi@0: this.beanInfo = beanInfo; aoqi@0: } aoqi@0: aoqi@0: /** aoqi@0: * Completes the initialization. aoqi@0: * aoqi@0: *

aoqi@0: * To fix the cyclic reference issue, the main part of the initialization needs to be done aoqi@0: * after a {@link StructureLoader} is set to {@link ClassBeanInfoImpl#loader}. aoqi@0: */ aoqi@0: public void init( JAXBContextImpl context, ClassBeanInfoImpl beanInfo, Accessor> attWildcard) { aoqi@0: UnmarshallerChain chain = new UnmarshallerChain(context); aoqi@0: for (ClassBeanInfoImpl bi = beanInfo; bi != null; bi = bi.superClazz) { aoqi@0: for (int i = bi.properties.length - 1; i >= 0; i--) { aoqi@0: Property p = bi.properties[i]; aoqi@0: aoqi@0: switch(p.getKind()) { aoqi@0: case ATTRIBUTE: aoqi@0: if(attUnmarshallers==null) aoqi@0: attUnmarshallers = new QNameMap(); aoqi@0: AttributeProperty ap = (AttributeProperty) p; aoqi@0: attUnmarshallers.put(ap.attName.toQName(),ap.xacc); aoqi@0: break; aoqi@0: case ELEMENT: aoqi@0: case REFERENCE: aoqi@0: case MAP: aoqi@0: case VALUE: aoqi@0: p.buildChildElementUnmarshallers(chain,childUnmarshallers); aoqi@0: break; aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: this.frameSize = chain.getScopeSize(); aoqi@0: aoqi@0: textHandler = childUnmarshallers.get(StructureLoaderBuilder.TEXT_HANDLER); aoqi@0: catchAll = childUnmarshallers.get(StructureLoaderBuilder.CATCH_ALL); aoqi@0: aoqi@0: if(attWildcard!=null) { aoqi@0: attCatchAll = (Accessor>) attWildcard; aoqi@0: // we use attUnmarshallers==null as a sign to skip the attribute processing aoqi@0: // altogether, so if we have an att wildcard we need to have an empty qname map. aoqi@0: if(attUnmarshallers==null) aoqi@0: attUnmarshallers = EMPTY; aoqi@0: } else { aoqi@0: attCatchAll = null; aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public void startElement(UnmarshallingContext.State state, TagName ea) throws SAXException { aoqi@0: UnmarshallingContext context = state.getContext(); aoqi@0: aoqi@0: // create the object to unmarshal aoqi@0: Object child; aoqi@0: assert !beanInfo.isImmutable(); aoqi@0: aoqi@0: // let's see if we can reuse the existing peer object aoqi@0: child = context.getInnerPeer(); aoqi@0: aoqi@0: if(child != null && beanInfo.jaxbType!=child.getClass()) aoqi@0: child = null; // unexpected type. aoqi@0: aoqi@0: if(child != null) aoqi@0: beanInfo.reset(child,context); aoqi@0: aoqi@0: if(child == null) aoqi@0: child = context.createInstance(beanInfo); aoqi@0: aoqi@0: context.recordInnerPeer(child); aoqi@0: aoqi@0: state.target = child; aoqi@0: aoqi@0: fireBeforeUnmarshal(beanInfo, child, state); aoqi@0: aoqi@0: aoqi@0: context.startScope(frameSize); aoqi@0: aoqi@0: if(attUnmarshallers!=null) { aoqi@0: Attributes atts = ea.atts; aoqi@0: for (int i = 0; i < atts.getLength(); i ++){ aoqi@0: String auri = atts.getURI(i); aoqi@0: // may be empty string based on parser settings aoqi@0: String alocal = atts.getLocalName(i); aoqi@0: if ("".equals(alocal)) { aoqi@0: alocal = atts.getQName(i); aoqi@0: } aoqi@0: String avalue = atts.getValue(i); aoqi@0: TransducedAccessor xacc = attUnmarshallers.get(auri, alocal); aoqi@0: try { aoqi@0: if(xacc!=null) { aoqi@0: xacc.parse(child,avalue); aoqi@0: } else if (attCatchAll!=null) { aoqi@0: String qname = atts.getQName(i); aoqi@0: if(atts.getURI(i).equals(WellKnownNamespace.XML_SCHEMA_INSTANCE)) aoqi@0: continue; // xsi:* attributes are meant to be processed by us, not by user apps. aoqi@0: Object o = state.target; aoqi@0: Map map = attCatchAll.get(o); aoqi@0: if(map==null) { aoqi@0: // TODO: use ClassFactory.inferImplClass(sig,knownImplClasses) aoqi@0: aoqi@0: // if null, create a new map. aoqi@0: if(attCatchAll.valueType.isAssignableFrom(HashMap.class)) aoqi@0: map = new HashMap(); aoqi@0: else { aoqi@0: // we don't know how to create a map for this. aoqi@0: // report an error and back out aoqi@0: context.handleError(Messages.UNABLE_TO_CREATE_MAP.format(attCatchAll.valueType)); aoqi@0: return; aoqi@0: } aoqi@0: attCatchAll.set(o,map); aoqi@0: } aoqi@0: aoqi@0: String prefix; aoqi@0: int idx = qname.indexOf(':'); aoqi@0: if(idx<0) prefix=""; aoqi@0: else prefix=qname.substring(0,idx); aoqi@0: aoqi@0: map.put(new QName(auri,alocal,prefix),avalue); aoqi@0: } aoqi@0: } catch (AccessorException e) { aoqi@0: handleGenericException(e,true); aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public void childElement(UnmarshallingContext.State state, TagName arg) throws SAXException { aoqi@0: ChildLoader child = childUnmarshallers.get(arg.uri,arg.local); aoqi@0: if(child==null) { aoqi@0: if ((beanInfo != null) && (beanInfo.getTypeNames() != null)) { aoqi@0: Iterator typeNamesIt = beanInfo.getTypeNames().iterator(); aoqi@0: QName parentQName = null; aoqi@0: if ((typeNamesIt != null) && (typeNamesIt.hasNext()) && (catchAll == null)) { aoqi@0: parentQName = (QName) typeNamesIt.next(); aoqi@0: String parentUri = parentQName.getNamespaceURI(); aoqi@0: child = childUnmarshallers.get(parentUri, arg.local); aoqi@0: } aoqi@0: } aoqi@0: if (child == null) { aoqi@0: child = catchAll; aoqi@0: if(child==null) { aoqi@0: super.childElement(state,arg); aoqi@0: return; aoqi@0: } aoqi@0: } aoqi@0: } aoqi@0: aoqi@0: state.loader = child.loader; aoqi@0: state.receiver = child.receiver; aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public Collection getExpectedChildElements() { aoqi@0: return childUnmarshallers.keySet(); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public Collection getExpectedAttributes() { aoqi@0: return attUnmarshallers.keySet(); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public void text(UnmarshallingContext.State state, CharSequence text) throws SAXException { aoqi@0: if(textHandler!=null) aoqi@0: textHandler.loader.text(state,text); aoqi@0: } aoqi@0: aoqi@0: @Override aoqi@0: public void leaveElement(UnmarshallingContext.State state, TagName ea) throws SAXException { aoqi@0: state.getContext().endScope(frameSize); aoqi@0: fireAfterUnmarshal(beanInfo, state.target, state.prev); aoqi@0: } aoqi@0: aoqi@0: private static final QNameMap EMPTY = new QNameMap(); aoqi@0: aoqi@0: public JaxBeanInfo getBeanInfo() { aoqi@0: return beanInfo; aoqi@0: } aoqi@0: }