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.reflect;
038:
039: import java.io.IOException;
040: import java.util.concurrent.Callable;
041:
042: import javax.xml.bind.JAXBException;
043: import javax.xml.bind.annotation.XmlValue;
044: import javax.xml.stream.XMLStreamException;
045:
046: import com.sun.istack.NotNull;
047: import com.sun.istack.Nullable;
048: import com.sun.istack.SAXException2;
049: import com.sun.xml.bind.WhiteSpaceProcessor;
050: import com.sun.xml.bind.api.AccessorException;
051: import com.sun.xml.bind.v2.model.core.ID;
052: import com.sun.xml.bind.v2.model.impl.RuntimeModelBuilder;
053: import com.sun.xml.bind.v2.model.nav.Navigator;
054: import com.sun.xml.bind.v2.model.runtime.RuntimeNonElementRef;
055: import com.sun.xml.bind.v2.model.runtime.RuntimePropertyInfo;
056: import com.sun.xml.bind.v2.runtime.Name;
057: import com.sun.xml.bind.v2.runtime.Transducer;
058: import com.sun.xml.bind.v2.runtime.XMLSerializer;
059: import com.sun.xml.bind.v2.runtime.JAXBContextImpl;
060: import com.sun.xml.bind.v2.runtime.reflect.opt.OptimizedTransducedAccessorFactory;
061: import com.sun.xml.bind.v2.runtime.unmarshaller.Patcher;
062: import com.sun.xml.bind.v2.runtime.unmarshaller.UnmarshallingContext;
063: import com.sun.xml.bind.v2.runtime.unmarshaller.LocatorEx;
064:
065: import org.xml.sax.SAXException;
066:
067: /**
068: * {@link Accessor} and {@link Transducer} combined into one object.
069: *
070: * <p>
071: * This allows efficient conversions between primitive values and
072: * String without using boxing.
073: *
074: * <p>
075: * This abstraction only works for a single-value property.
076: *
077: * <p>
078: * An instance of {@link TransducedAccessor} implicitly holds a
079: * field of the {@code BeanT} that the accessors access.
080: *
081: * @author Kohsuke Kawaguchi (kohsuke.kawaguchi@sun.com)
082: */
083: public abstract class TransducedAccessor<BeanT> {
084:
085: /**
086: * @see Transducer#useNamespace()
087: */
088: public boolean useNamespace() {
089: return false;
090: }
091:
092: /**
093: * Obtain the value of the field and declares the namespace URIs used in
094: * the value.
095: *
096: * @see Transducer#declareNamespace(Object, XMLSerializer)
097: */
098: public void declareNamespace(BeanT o, XMLSerializer w)
099: throws AccessorException, SAXException {
100: }
101:
102: /**
103: * Prints the responsible field of the given bean to the writer.
104: *
105: * <p>
106: * Use {@link XMLSerializer#getInstance()} to access to the namespace bindings
107: *
108: * @return
109: * if the accessor didn't yield a value, return null.
110: */
111: public abstract @Nullable
112: CharSequence print(@NotNull
113: BeanT o) throws AccessorException, SAXException;
114:
115: /**
116: * Parses the text value into the responsible field of the given bean.
117: *
118: * <p>
119: * Use {@link UnmarshallingContext#getInstance()} to access to the namespace bindings
120: *
121: * @throws AccessorException
122: * if the transducer is used to parse an user bean that uses {@link XmlValue},
123: * then this exception may occur when it tries to set the leaf value to the bean.
124: * @throws RuntimeException
125: * if the lexical form is incorrect. The method may throw a RuntimeException,
126: * but it shouldn't cause the entire unmarshalling to fail.
127: * @throws SAXException
128: * if the parse method found an error, the error is reported, and then
129: * the processing is aborted.
130: */
131: public abstract void parse(BeanT o, CharSequence lexical)
132: throws AccessorException, SAXException;
133:
134: /**
135: * Checks if the field has a value.
136: */
137: public abstract boolean hasValue(BeanT o) throws AccessorException;
138:
139: /**
140: * Gets the {@link TransducedAccessor} appropriately configured for
141: * the given property.
142: *
143: * <p>
144: * This allows the implementation to use an optimized code.
145: */
146: public static <T> TransducedAccessor<T> get(
147: JAXBContextImpl context, RuntimeNonElementRef ref) {
148: Transducer xducer = RuntimeModelBuilder.createTransducer(ref);
149: RuntimePropertyInfo prop = ref.getSource();
150:
151: if (prop.isCollection()) {
152: return new ListTransducedAccessorImpl(xducer, prop
153: .getAccessor(), Lister.create(Navigator.REFLECTION
154: .erasure(prop.getRawType()), prop.id(), prop
155: .getAdapter()));
156: }
157:
158: if (prop.id() == ID.IDREF)
159: return new IDREFTransducedAccessorImpl(prop.getAccessor());
160:
161: if (xducer.isDefault() && !context.fastBoot) {
162: TransducedAccessor xa = OptimizedTransducedAccessorFactory
163: .get(prop);
164: if (xa != null)
165: return xa;
166: }
167:
168: if (xducer.useNamespace())
169: return new CompositeContextDependentTransducedAccessorImpl(
170: context, xducer, prop.getAccessor());
171: else
172: return new CompositeTransducedAccessorImpl(context, xducer,
173: prop.getAccessor());
174: }
175:
176: /**
177: * Convenience method to write the value as a text inside an element
178: * without any attributes.
179: * Can be overridden for improved performance.
180: *
181: * <p>
182: * The callee assumes that there's an associated value in the field.
183: * No @xsi:type handling is expected.
184: */
185: public abstract void writeLeafElement(XMLSerializer w,
186: Name tagName, BeanT o, String fieldName)
187: throws SAXException, AccessorException, IOException,
188: XMLStreamException;
189:
190: /**
191: * Invokes one of the {@link XMLSerializer#text(String, String)} method
192: * with the representation of data bested suited for this transduced accessor.
193: */
194: public abstract void writeText(XMLSerializer w, BeanT o,
195: String fieldName) throws AccessorException, SAXException,
196: IOException, XMLStreamException;
197:
198: static class CompositeContextDependentTransducedAccessorImpl<BeanT, ValueT>
199: extends CompositeTransducedAccessorImpl<BeanT, ValueT> {
200: public CompositeContextDependentTransducedAccessorImpl(
201: JAXBContextImpl context, Transducer<ValueT> xducer,
202: Accessor<BeanT, ValueT> acc) {
203: super (context, xducer, acc);
204: assert xducer.useNamespace();
205: }
206:
207: public boolean useNamespace() {
208: return true;
209: }
210:
211: public void declareNamespace(BeanT bean, XMLSerializer w)
212: throws AccessorException {
213: ValueT o = acc.get(bean);
214: if (o != null)
215: xducer.declareNamespace(o, w);
216: }
217:
218: @Override
219: public void writeLeafElement(XMLSerializer w, Name tagName,
220: BeanT o, String fieldName) throws SAXException,
221: AccessorException, IOException, XMLStreamException {
222: w.startElement(tagName, null);
223: declareNamespace(o, w);
224: w.endNamespaceDecls(null);
225: w.endAttributes();
226: xducer.writeText(w, acc.get(o), fieldName);
227: w.endElement();
228: }
229: }
230:
231: /**
232: * Implementation of {@link TransducedAccessor} that
233: * simply combines a {@link Transducer} and {@link Accessor}.
234: */
235: static class CompositeTransducedAccessorImpl<BeanT, ValueT> extends
236: TransducedAccessor<BeanT> {
237: protected final Transducer<ValueT> xducer;
238: protected final Accessor<BeanT, ValueT> acc;
239:
240: public CompositeTransducedAccessorImpl(JAXBContextImpl context,
241: Transducer<ValueT> xducer, Accessor<BeanT, ValueT> acc) {
242: this .xducer = xducer;
243: this .acc = acc.optimize(context);
244: }
245:
246: public CharSequence print(BeanT bean) throws AccessorException {
247: ValueT o = acc.get(bean);
248: if (o == null)
249: return null;
250: return xducer.print(o);
251: }
252:
253: public void parse(BeanT bean, CharSequence lexical)
254: throws AccessorException, SAXException {
255: acc.set(bean, xducer.parse(lexical));
256: }
257:
258: public boolean hasValue(BeanT bean) throws AccessorException {
259: return acc.getUnadapted(bean) != null;
260: }
261:
262: @Override
263: public void writeLeafElement(XMLSerializer w, Name tagName,
264: BeanT o, String fieldName) throws SAXException,
265: AccessorException, IOException, XMLStreamException {
266: xducer.writeLeafElement(w, tagName, acc.get(o), fieldName);
267: }
268:
269: @Override
270: public void writeText(XMLSerializer w, BeanT o, String fieldName)
271: throws AccessorException, SAXException, IOException,
272: XMLStreamException {
273: xducer.writeText(w, acc.get(o), fieldName);
274: }
275: }
276:
277: /**
278: * {@link TransducedAccessor} for IDREF.
279: *
280: * BeanT: the type of the bean that contains this the IDREF field.
281: * TargetT: the type of the bean pointed by IDREF.
282: */
283: private static final class IDREFTransducedAccessorImpl<BeanT, TargetT>
284: extends DefaultTransducedAccessor<BeanT> {
285: private final Accessor<BeanT, TargetT> acc;
286: /**
287: * The object that an IDREF resolves to should be
288: * assignable to this type.
289: */
290: private final Class<TargetT> targetType;
291:
292: public IDREFTransducedAccessorImpl(Accessor<BeanT, TargetT> acc) {
293: this .acc = acc;
294: this .targetType = acc.getValueType();
295: }
296:
297: public String print(BeanT bean) throws AccessorException,
298: SAXException {
299: TargetT target = acc.get(bean);
300: if (target == null)
301: return null;
302:
303: XMLSerializer w = XMLSerializer.getInstance();
304: try {
305: String id = w.grammar.getBeanInfo(target, true).getId(
306: target, w);
307: if (id == null)
308: w.errorMissingId(target);
309: return id;
310: } catch (JAXBException e) {
311: w.reportError(null, e);
312: return null;
313: }
314: }
315:
316: private void assign(BeanT bean, TargetT t,
317: UnmarshallingContext context) throws AccessorException {
318: if (!targetType.isInstance(t))
319: context.handleError(Messages.UNASSIGNABLE_TYPE.format(
320: targetType, t.getClass()));
321: else
322: acc.set(bean, t);
323: }
324:
325: public void parse(final BeanT bean, CharSequence lexical)
326: throws AccessorException, SAXException {
327: final String idref = WhiteSpaceProcessor.trim(lexical)
328: .toString();
329: final UnmarshallingContext context = UnmarshallingContext
330: .getInstance();
331:
332: final Callable callable = context.getObjectFromId(idref,
333: acc.valueType);
334: if (callable == null) {
335: // the IDResolver decided to abort it now
336: context.errorUnresolvedIDREF(bean, idref, context
337: .getLocator());
338: return;
339: }
340:
341: TargetT t;
342: try {
343: t = (TargetT) callable.call();
344: } catch (SAXException e) {// from callable.call
345: throw e;
346: } catch (RuntimeException e) {// from callable.call
347: throw e;
348: } catch (Exception e) {// from callable.call
349: throw new SAXException2(e);
350: }
351: if (t != null) {
352: assign(bean, t, context);
353: } else {
354: // try again later
355: final LocatorEx loc = new LocatorEx.Snapshot(context
356: .getLocator());
357: context.addPatcher(new Patcher() {
358: public void run() throws SAXException {
359: try {
360: TargetT t = (TargetT) callable.call();
361: if (t == null) {
362: context.errorUnresolvedIDREF(bean,
363: idref, loc);
364: } else {
365: assign(bean, t, context);
366: }
367: } catch (AccessorException e) {
368: context.handleError(e);
369: } catch (SAXException e) {// from callable.call
370: throw e;
371: } catch (RuntimeException e) {// from callable.call
372: throw e;
373: } catch (Exception e) {// from callable.call
374: throw new SAXException2(e);
375: }
376: }
377: });
378: }
379: }
380:
381: public boolean hasValue(BeanT bean) throws AccessorException {
382: return acc.get(bean) != null;
383: }
384: }
385: }
|