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;
038:
039: import java.io.IOException;
040: import java.lang.reflect.Constructor;
041: import java.lang.reflect.InvocationTargetException;
042:
043: import javax.xml.bind.JAXBElement;
044: import javax.xml.bind.JAXBException;
045: import javax.xml.namespace.QName;
046: import javax.xml.stream.XMLStreamException;
047:
048: import com.sun.xml.bind.api.AccessorException;
049: import com.sun.xml.bind.v2.model.core.PropertyKind;
050: import com.sun.xml.bind.v2.model.nav.Navigator;
051: import com.sun.xml.bind.v2.model.runtime.RuntimeElementInfo;
052: import com.sun.xml.bind.v2.runtime.property.Property;
053: import com.sun.xml.bind.v2.runtime.property.PropertyFactory;
054: import com.sun.xml.bind.v2.runtime.property.UnmarshallerChain;
055: import com.sun.xml.bind.v2.runtime.reflect.Accessor;
056: import com.sun.xml.bind.v2.runtime.unmarshaller.ChildLoader;
057: import com.sun.xml.bind.v2.runtime.unmarshaller.Discarder;
058: import com.sun.xml.bind.v2.runtime.unmarshaller.Intercepter;
059: import com.sun.xml.bind.v2.runtime.unmarshaller.Loader;
060: import com.sun.xml.bind.v2.runtime.unmarshaller.TagName;
061: import com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext;
062: import com.sun.xml.bind.v2.util.QNameMap;
063:
064: import org.xml.sax.SAXException;
065:
066: /**
067: * {@link JaxBeanInfo} implementation for {@link RuntimeElementInfo}.
068: *
069: * @author Kohsuke Kawaguchi
070: */
071: public final class ElementBeanInfoImpl extends JaxBeanInfo<JAXBElement> {
072:
073: private Loader loader;
074:
075: private final Property property;
076:
077: // used to create new instances of JAXBElement.
078: private final QName tagName;
079: public final Class expectedType;
080: private final Class scope;
081:
082: /**
083: * If non-null, use this to create an instance.
084: * It takes one value.
085: */
086: private final Constructor<? extends JAXBElement> constructor;
087:
088: ElementBeanInfoImpl(JAXBContextImpl grammar, RuntimeElementInfo rei) {
089: super (grammar, rei, (Class<JAXBElement>) rei.getType(), true,
090: false, true);
091:
092: this .property = PropertyFactory.create(grammar, rei
093: .getProperty());
094:
095: tagName = rei.getElementName();
096: expectedType = Navigator.REFLECTION.erasure(rei
097: .getContentInMemoryType());
098: scope = rei.getScope() == null ? JAXBElement.GlobalScope.class
099: : rei.getScope().getClazz();
100:
101: Class type = Navigator.REFLECTION.erasure(rei.getType());
102: if (type == JAXBElement.class)
103: constructor = null;
104: else {
105: try {
106: constructor = type.getConstructor(expectedType);
107: } catch (NoSuchMethodException e) {
108: NoSuchMethodError x = new NoSuchMethodError(
109: "Failed to find the constructor for " + type
110: + " with " + expectedType);
111: x.initCause(e);
112: throw x;
113: }
114: }
115: }
116:
117: /**
118: * The constructor for the sole instanceof {@link JaxBeanInfo} for
119: * handling user-created {@link JAXBElement}.
120: *
121: * Such {@link JaxBeanInfo} is used only for marshalling.
122: *
123: * This is a hack.
124: */
125: protected ElementBeanInfoImpl(final JAXBContextImpl grammar) {
126: super (grammar, null, JAXBElement.class, true, false, true);
127: tagName = null;
128: expectedType = null;
129: scope = null;
130: constructor = null;
131:
132: this .property = new Property<JAXBElement>() {
133: public void reset(JAXBElement o) {
134: throw new UnsupportedOperationException();
135: }
136:
137: public void serializeBody(JAXBElement e,
138: XMLSerializer target, Object outerPeer)
139: throws SAXException, IOException,
140: XMLStreamException {
141: Class scope = e.getScope();
142: if (e.isGlobalScope())
143: scope = null;
144: QName n = e.getName();
145: ElementBeanInfoImpl bi = grammar.getElement(scope, n);
146: if (bi == null) {
147: // infer what to do from the type
148: JaxBeanInfo tbi;
149: try {
150: tbi = grammar.getBeanInfo(e.getDeclaredType(),
151: true);
152: } catch (JAXBException x) {
153: // if e.getDeclaredType() isn't known to this JAXBContext
154: target.reportError(null, x);
155: return;
156: }
157: Object value = e.getValue();
158: target.startElement(n.getNamespaceURI(), n
159: .getLocalPart(), n.getPrefix(), null);
160: if (value == null) {
161: target.writeXsiNilTrue();
162: } else {
163: target.childAsXsiType(value, "value", tbi);
164: }
165: target.endElement();
166: } else {
167: try {
168: bi.property.serializeBody(e, target, e);
169: } catch (AccessorException x) {
170: target.reportError(null, x);
171: }
172: }
173: }
174:
175: public void serializeURIs(JAXBElement o,
176: XMLSerializer target) {
177: }
178:
179: public boolean hasSerializeURIAction() {
180: return false;
181: }
182:
183: public String getIdValue(JAXBElement o) {
184: return null;
185: }
186:
187: public PropertyKind getKind() {
188: return PropertyKind.ELEMENT;
189: }
190:
191: public void buildChildElementUnmarshallers(
192: UnmarshallerChain chain,
193: QNameMap<ChildLoader> handlers) {
194: }
195:
196: public Accessor getElementPropertyAccessor(String nsUri,
197: String localName) {
198: throw new UnsupportedOperationException();
199: }
200:
201: public void wrapUp() {
202: }
203: };
204: }
205:
206: /**
207: * Use the previous {@link UnmarshallingContext.State}'s target to store
208: * {@link JAXBElement} object to be unmarshalled. This allows the property {@link Loader}
209: * to correctly find the parent object.
210: * This is a hack.
211: */
212: private final class IntercepterLoader extends Loader implements
213: Intercepter {
214: private final Loader core;
215:
216: public IntercepterLoader(Loader core) {
217: this .core = core;
218: }
219:
220: public final void startElement(
221: UnmarshallingContext.State state, TagName ea)
222: throws SAXException {
223: state.loader = core;
224: state.intercepter = this ;
225:
226: // TODO: make sure there aren't too many duplicate of this code
227: // create the object to unmarshal
228: Object child;
229: UnmarshallingContext context = state.getContext();
230:
231: // let's see if we can reuse the existing peer object
232: child = context.getOuterPeer();
233:
234: if (child != null && jaxbType != child.getClass())
235: child = null; // unexpected type.
236:
237: if (child != null)
238: reset((JAXBElement) child, context);
239:
240: if (child == null)
241: child = context
242: .createInstance(ElementBeanInfoImpl.this );
243:
244: fireBeforeUnmarshal(ElementBeanInfoImpl.this , child, state);
245:
246: context.recordOuterPeer(child);
247: UnmarshallingContext.State p = state.prev;
248: p.backup = p.target;
249: p.target = child;
250:
251: core.startElement(state, ea);
252: }
253:
254: public Object intercept(UnmarshallingContext.State state,
255: Object o) throws SAXException {
256: JAXBElement e = (JAXBElement) state.target;
257: state.target = state.backup;
258: state.backup = null;
259:
260: if (o != null)
261: // if the value is a leaf type, it's often already set to the element
262: // through Accessor.
263: e.setValue(o);
264:
265: fireAfterUnmarshal(ElementBeanInfoImpl.this , e, state);
266:
267: return e;
268: }
269: }
270:
271: public String getElementNamespaceURI(JAXBElement e) {
272: return e.getName().getNamespaceURI();
273: }
274:
275: public String getElementLocalName(JAXBElement e) {
276: return e.getName().getLocalPart();
277: }
278:
279: public Loader getLoader(JAXBContextImpl context,
280: boolean typeSubstitutionCapable) {
281: if (loader == null) {
282: // this has to be done lazily to avoid cyclic reference issue
283: UnmarshallerChain c = new UnmarshallerChain(context);
284: QNameMap<ChildLoader> result = new QNameMap<ChildLoader>();
285: property.buildChildElementUnmarshallers(c, result);
286: if (result.size() == 1)
287: // for ElementBeanInfoImpl created from RuntimeElementInfo
288: this .loader = new IntercepterLoader(result.getOne()
289: .getValue().loader);
290: else
291: // for special ElementBeanInfoImpl only used for marshalling
292: this .loader = Discarder.INSTANCE;
293: }
294: return loader;
295: }
296:
297: public final JAXBElement createInstance(UnmarshallingContext context)
298: throws IllegalAccessException, InvocationTargetException,
299: InstantiationException {
300: return createInstanceFromValue(null);
301: }
302:
303: public final JAXBElement createInstanceFromValue(Object o)
304: throws IllegalAccessException, InvocationTargetException,
305: InstantiationException {
306: if (constructor == null)
307: return new JAXBElement(tagName, expectedType, scope, o);
308: else
309: return constructor.newInstance(o);
310: }
311:
312: public boolean reset(JAXBElement e, UnmarshallingContext context) {
313: e.setValue(null);
314: return true;
315: }
316:
317: public String getId(JAXBElement e, XMLSerializer target) {
318: // TODO: is this OK? Should we be returning the ID value of the type property?
319: /*
320: There's one case where we JAXBElement needs to be designated as ID,
321: and that is when there's a global element whose type is ID.
322: */
323: Object o = e.getValue();
324: if (o instanceof String)
325: return (String) o;
326: else
327: return null;
328: }
329:
330: public void serializeBody(JAXBElement element, XMLSerializer target)
331: throws SAXException, IOException, XMLStreamException {
332: try {
333: property.serializeBody(element, target, null);
334: } catch (AccessorException x) {
335: target.reportError(null, x);
336: }
337: }
338:
339: public void serializeRoot(JAXBElement e, XMLSerializer target)
340: throws SAXException, IOException, XMLStreamException {
341: serializeBody(e, target);
342: }
343:
344: public void serializeAttributes(JAXBElement e, XMLSerializer target) {
345: // noop
346: }
347:
348: public void serializeURIs(JAXBElement e, XMLSerializer target) {
349: // noop
350: }
351:
352: public final Transducer<JAXBElement> getTransducer() {
353: return null;
354: }
355:
356: public void wrapUp() {
357: super .wrapUp();
358: property.wrapUp();
359: }
360:
361: public void link(JAXBContextImpl grammar) {
362: super .link(grammar);
363: getLoader(grammar, true); // make sure to build them, if we hadn't done so
364: }
365: }
|