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