001: /*
002: * Copyright 2006 Sun Microsystems, Inc. All Rights Reserved.
003: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
004: *
005: * This code is free software; you can redistribute it and/or modify it
006: * under the terms of the GNU General Public License version 2 only, as
007: * published by the Free Software Foundation. Sun designates this
008: * particular file as subject to the "Classpath" exception as provided
009: * by Sun in the LICENSE file that accompanied this code.
010: *
011: * This code is distributed in the hope that it will be useful, but WITHOUT
012: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
013: * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
014: * version 2 for more details (a copy is included in the LICENSE file that
015: * accompanied this code).
016: *
017: * You should have received a copy of the GNU General Public License version
018: * 2 along with this work; if not, write to the Free Software Foundation,
019: * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
020: *
021: * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara,
022: * CA 95054 USA or visit www.sun.com if you need additional information or
023: * have any questions.
024: */
025:
026: package com.sun.xml.internal.bind.v2.runtime.unmarshaller;
027:
028: import java.util.Collection;
029: import java.util.HashMap;
030: import java.util.Map;
031:
032: import javax.xml.namespace.QName;
033:
034: import com.sun.xml.internal.bind.api.AccessorException;
035: import com.sun.xml.internal.bind.v2.WellKnownNamespace;
036: import com.sun.xml.internal.bind.v2.runtime.ClassBeanInfoImpl;
037: import com.sun.xml.internal.bind.v2.runtime.JAXBContextImpl;
038: import com.sun.xml.internal.bind.v2.runtime.JaxBeanInfo;
039: import com.sun.xml.internal.bind.v2.runtime.property.AttributeProperty;
040: import com.sun.xml.internal.bind.v2.runtime.property.Property;
041: import com.sun.xml.internal.bind.v2.runtime.property.StructureLoaderBuilder;
042: import com.sun.xml.internal.bind.v2.runtime.property.UnmarshallerChain;
043: import com.sun.xml.internal.bind.v2.runtime.reflect.Accessor;
044: import com.sun.xml.internal.bind.v2.runtime.reflect.TransducedAccessor;
045: import com.sun.xml.internal.bind.v2.util.QNameMap;
046:
047: import org.xml.sax.Attributes;
048: import org.xml.sax.SAXException;
049:
050: /**
051: * Loads children of an element.
052: *
053: * <p>
054: * This loader works with a single {@link JaxBeanInfo} and handles
055: * attributes, child elements, or child text.
056: *
057: * @author Kohsuke Kawaguchi
058: */
059: public final class StructureLoader extends Loader {
060: /**
061: * This map statically stores information of the
062: * unmarshaller loader and can be used while unmarshalling
063: * Since creating new QNames is expensive use this optimized
064: * version of the map
065: */
066: private final QNameMap<ChildLoader> childUnmarshallers = new QNameMap<ChildLoader>();
067:
068: /**
069: * Loader that processes elements that didn't match anf of the {@link #childUnmarshallers}.
070: * Can be null.
071: */
072: private/*final*/ChildLoader catchAll;
073:
074: /**
075: * If we have a loader for processing text. Otherwise null.
076: */
077: private/*final*/ChildLoader textHandler;
078:
079: /**
080: * Unmarshallers for attribute values.
081: * May be null if no attribute is expected and {@link #attCatchAll}==null.
082: */
083: private/*final*/QNameMap<TransducedAccessor> attUnmarshallers;
084:
085: /**
086: * This will receive all the attributes
087: * that were not processed. Never be null.
088: */
089: private/*final*/Accessor<Object, Map<QName, String>> attCatchAll;
090:
091: private final JaxBeanInfo beanInfo;
092:
093: /**
094: * The number of scopes this dispatcher needs to keep active.
095: */
096: private/*final*/int frameSize;
097:
098: // this class is potentially useful for general audience, not just for ClassBeanInfoImpl,
099: // but since right now that is the only user, we make the construction code very specific
100: // to ClassBeanInfoImpl. See rev.1.5 of this file for the original general purpose definition.
101: public StructureLoader(ClassBeanInfoImpl beanInfo) {
102: super (true);
103: this .beanInfo = beanInfo;
104: }
105:
106: /**
107: * Completes the initialization.
108: *
109: * <p>
110: * To fix the cyclic reference issue, the main part of the initialization needs to be done
111: * after a {@link StructureLoader} is set to {@link ClassBeanInfoImpl#loader}.
112: */
113: public void init(JAXBContextImpl context,
114: ClassBeanInfoImpl beanInfo,
115: Accessor<?, Map<QName, String>> attWildcard) {
116: UnmarshallerChain chain = new UnmarshallerChain(context);
117: for (ClassBeanInfoImpl bi = beanInfo; bi != null; bi = bi.super Clazz) {
118: for (int i = bi.properties.length - 1; i >= 0; i--) {
119: Property p = bi.properties[i];
120:
121: switch (p.getKind()) {
122: case ATTRIBUTE:
123: if (attUnmarshallers == null)
124: attUnmarshallers = new QNameMap<TransducedAccessor>();
125: AttributeProperty ap = (AttributeProperty) p;
126: attUnmarshallers.put(ap.attName.toQName(), ap.xacc);
127: break;
128: case ELEMENT:
129: case REFERENCE:
130: case MAP:
131: case VALUE:
132: p.buildChildElementUnmarshallers(chain,
133: childUnmarshallers);
134: break;
135: }
136: }
137: }
138:
139: this .frameSize = chain.getScopeSize();
140:
141: textHandler = childUnmarshallers
142: .get(StructureLoaderBuilder.TEXT_HANDLER);
143: catchAll = childUnmarshallers
144: .get(StructureLoaderBuilder.CATCH_ALL);
145:
146: if (attWildcard != null) {
147: attCatchAll = (Accessor<Object, Map<QName, String>>) attWildcard;
148: // we use attUnmarshallers==null as a sign to skip the attribute processing
149: // altogether, so if we have an att wildcard we need to have an empty qname map.
150: if (attUnmarshallers == null)
151: attUnmarshallers = EMPTY;
152: } else {
153: attCatchAll = null;
154: }
155: }
156:
157: @Override
158: public void startElement(UnmarshallingContext.State state,
159: TagName ea) throws SAXException {
160: UnmarshallingContext context = state.getContext();
161:
162: // create the object to unmarshal
163: Object child;
164: assert !beanInfo.isImmutable();
165:
166: // let's see if we can reuse the existing peer object
167: child = context.getInnerPeer();
168:
169: if (child != null && beanInfo.jaxbType != child.getClass())
170: child = null; // unexpected type.
171:
172: if (child != null)
173: beanInfo.reset(child, context);
174:
175: if (child == null)
176: child = context.createInstance(beanInfo);
177:
178: context.recordInnerPeer(child);
179:
180: state.target = child;
181:
182: fireBeforeUnmarshal(beanInfo, child, state);
183:
184: context.startScope(frameSize);
185:
186: if (attUnmarshallers != null) {
187: Attributes atts = ea.atts;
188: for (int i = 0; i < atts.getLength(); i++) {
189: String auri = atts.getURI(i);
190: String alocal = atts.getLocalName(i);
191: String avalue = atts.getValue(i);
192: TransducedAccessor xacc = attUnmarshallers.get(auri,
193: alocal);
194:
195: try {
196: if (xacc != null) {
197: xacc.parse(child, avalue);
198: } else if (attCatchAll != null) {
199: String qname = atts.getQName(i);
200: if (atts.getURI(i).equals(
201: WellKnownNamespace.XML_SCHEMA_INSTANCE))
202: continue; // xsi:* attributes are meant to be processed by us, not by user apps.
203: Object o = state.target;
204: Map<QName, String> map = attCatchAll.get(o);
205: if (map == null) {
206: // TODO: use ClassFactory.inferImplClass(sig,knownImplClasses)
207:
208: // if null, create a new map.
209: if (attCatchAll.valueType
210: .isAssignableFrom(HashMap.class))
211: map = new HashMap<QName, String>();
212: else {
213: // we don't know how to create a map for this.
214: // report an error and back out
215: context
216: .handleError(Messages.UNABLE_TO_CREATE_MAP
217: .format(attCatchAll.valueType));
218: return;
219: }
220: attCatchAll.set(o, map);
221: }
222:
223: String prefix;
224: int idx = qname.indexOf(':');
225: if (idx < 0)
226: prefix = "";
227: else
228: prefix = qname.substring(0, idx);
229:
230: map
231: .put(new QName(auri, alocal, prefix),
232: avalue);
233: }
234: } catch (AccessorException e) {
235: handleGenericException(e, true);
236: }
237: }
238: }
239: }
240:
241: @Override
242: public void childElement(UnmarshallingContext.State state,
243: TagName arg) throws SAXException {
244: ChildLoader child = childUnmarshallers.get(arg.uri, arg.local);
245: if (child == null) {
246: child = catchAll;
247: if (child == null) {
248: super .childElement(state, arg);
249: return;
250: }
251: }
252:
253: state.loader = child.loader;
254: state.receiver = child.receiver;
255: }
256:
257: @Override
258: public Collection<QName> getExpectedChildElements() {
259: return childUnmarshallers.keySet();
260: }
261:
262: @Override
263: public void text(UnmarshallingContext.State state, CharSequence text)
264: throws SAXException {
265: if (textHandler != null)
266: textHandler.loader.text(state, text);
267: }
268:
269: public void leaveElement(UnmarshallingContext.State state,
270: TagName ea) throws SAXException {
271: state.getContext().endScope(frameSize);
272: fireAfterUnmarshal(beanInfo, state.target, state.prev);
273: }
274:
275: private static final QNameMap<TransducedAccessor> EMPTY = new QNameMap<TransducedAccessor>();
276: }
|