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