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.lang.ref.WeakReference;
029: import java.lang.reflect.Array;
030: import java.lang.reflect.ParameterizedType;
031: import java.lang.reflect.Type;
032: import java.util.ArrayList;
033: import java.util.Collection;
034: import java.util.Collections;
035: import java.util.HashMap;
036: import java.util.Iterator;
037: import java.util.List;
038: import java.util.Map;
039: import java.util.WeakHashMap;
040: import java.util.concurrent.Callable;
041:
042: import javax.xml.bind.JAXBException;
043:
044: import com.sun.istack.internal.SAXException2;
045: import com.sun.xml.internal.bind.api.AccessorException;
046: import com.sun.xml.internal.bind.v2.ClassFactory;
047: import com.sun.xml.internal.bind.v2.TODO;
048: import com.sun.xml.internal.bind.v2.model.core.Adapter;
049: import com.sun.xml.internal.bind.v2.model.core.ID;
050: import com.sun.xml.internal.bind.v2.model.nav.Navigator;
051: import com.sun.xml.internal.bind.v2.runtime.XMLSerializer;
052: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.Patcher;
053: import com.sun.xml.internal.bind.v2.runtime.unmarshaller.UnmarshallingContext;
054:
055: import org.xml.sax.SAXException;
056:
057: /**
058: * Used to list individual values of a multi-value property, and
059: * to pack individual values into a multi-value property.
060: *
061: * @author Kohsuke Kawaguchi (kk@kohsuke.org)
062: */
063: public abstract class Lister<BeanT, PropT, ItemT, PackT> {
064:
065: protected Lister() {
066: }
067:
068: /**
069: * Iterates values of a multi-value property.
070: *
071: * @param context
072: * This parameter is used to support ID/IDREF handling.
073: */
074: public abstract ListIterator<ItemT> iterator(PropT multiValueProp,
075: XMLSerializer context);
076:
077: /**
078: * Setting values to a multi-value property starts by creating
079: * a transient object called "pack" from the current field.
080: */
081: public abstract PackT startPacking(BeanT bean,
082: Accessor<BeanT, PropT> acc) throws AccessorException;
083:
084: /**
085: * Once the {@link #startPacking} is called, you can
086: * add values to the pack by using this method.
087: */
088: public abstract void addToPack(PackT pack, ItemT newValue)
089: throws AccessorException;
090:
091: /**
092: * Finally, call this method to
093: * wraps up the {@code pack}. This method may update the field of
094: * the given bean.
095: */
096: public abstract void endPacking(PackT pack, BeanT bean,
097: Accessor<BeanT, PropT> acc) throws AccessorException;
098:
099: /**
100: * Clears the values of the property.
101: */
102: public abstract void reset(BeanT o, Accessor<BeanT, PropT> acc)
103: throws AccessorException;
104:
105: /**
106: * Gets a reference to the appropriate {@link Lister} object
107: * if the field is a multi-value field. Otherwise null.
108: *
109: * @param fieldType
110: * the type of the field that stores the collection
111: * @param idness
112: * ID-ness of the property.
113: * @param adapter
114: * adapter to be used for individual items. can be null.
115: */
116: public static <BeanT, PropT, ItemT, PackT> Lister<BeanT, PropT, ItemT, PackT> create(
117: Type fieldType, ID idness, Adapter<Type, Class> adapter) {
118:
119: Class rawType = Navigator.REFLECTION.erasure(fieldType);
120: Class itemType;
121:
122: Lister l;
123: if (rawType.isArray()) {
124: itemType = rawType.getComponentType();
125: l = getArrayLister(itemType);
126: } else if (Collection.class.isAssignableFrom(rawType)) {
127: Type bt = Navigator.REFLECTION.getBaseClass(fieldType,
128: Collection.class);
129: if (bt instanceof ParameterizedType)
130: itemType = Navigator.REFLECTION
131: .erasure(((ParameterizedType) bt)
132: .getActualTypeArguments()[0]);
133: else
134: itemType = Object.class;
135: l = new CollectionLister(getImplClass(rawType));
136: } else
137: return null;
138:
139: if (idness == ID.IDREF)
140: l = new IDREFS(l, itemType);
141:
142: if (adapter != null)
143: l = new AdaptedLister(l, adapter.adapterType);
144:
145: return l;
146: }
147:
148: private static Class getImplClass(Class fieldType) {
149: return ClassFactory.inferImplClass(fieldType,
150: ClassFactory.COLLECTION_IMPL_CLASSES);
151: }
152:
153: /**
154: * Cache instances of {@link ArrayLister}s.
155: */
156: private static final Map<Class, WeakReference<Lister>> arrayListerCache = Collections
157: .synchronizedMap(new WeakHashMap<Class, WeakReference<Lister>>());
158:
159: /**
160: * Creates a lister for array type.
161: */
162: private static Lister getArrayLister(Class componentType) {
163: Lister l = null;
164: if (componentType.isPrimitive())
165: l = primitiveArrayListers.get(componentType);
166: else {
167: WeakReference<Lister> wr = arrayListerCache
168: .get(componentType);
169: if (wr != null)
170: l = wr.get();
171: if (l == null) {
172: l = new ArrayLister(componentType);
173: arrayListerCache.put(componentType,
174: new WeakReference<Lister>(l));
175: }
176: }
177: assert l != null;
178: return l;
179: }
180:
181: /**
182: * {@link Lister} for an array.
183: *
184: * <p>
185: * Array packing is slower, but we expect this to be used less frequently than
186: * the {@link CollectionLister}.
187: */
188: private static final class ArrayLister<BeanT, ItemT> extends
189: Lister<BeanT, ItemT[], ItemT, Pack<ItemT>> {
190:
191: private final Class<ItemT> itemType;
192:
193: public ArrayLister(Class<ItemT> itemType) {
194: this .itemType = itemType;
195: }
196:
197: public ListIterator<ItemT> iterator(final ItemT[] objects,
198: XMLSerializer context) {
199: return new ListIterator<ItemT>() {
200: int idx = 0;
201:
202: public boolean hasNext() {
203: return idx < objects.length;
204: }
205:
206: public ItemT next() {
207: return objects[idx++];
208: }
209: };
210: }
211:
212: public Pack startPacking(BeanT current,
213: Accessor<BeanT, ItemT[]> acc) {
214: return new Pack<ItemT>(itemType);
215: }
216:
217: public void addToPack(Pack<ItemT> objects, ItemT o) {
218: objects.add(o);
219: }
220:
221: public void endPacking(Pack<ItemT> pack, BeanT bean,
222: Accessor<BeanT, ItemT[]> acc) throws AccessorException {
223: acc.set(bean, pack.build());
224: }
225:
226: public void reset(BeanT o, Accessor<BeanT, ItemT[]> acc)
227: throws AccessorException {
228: acc.set(o, (ItemT[]) Array.newInstance(itemType, 0));
229: }
230:
231: }
232:
233: public static final class Pack<ItemT> extends ArrayList<ItemT> {
234: private final Class<ItemT> itemType;
235:
236: public Pack(Class<ItemT> itemType) {
237: this .itemType = itemType;
238: }
239:
240: public ItemT[] build() {
241: return super .toArray((ItemT[]) Array.newInstance(itemType,
242: size()));
243: }
244: }
245:
246: /**
247: * Listers for the primitive type arrays, keyed by their primitive Class object.
248: */
249: /*package*/static final Map<Class, Lister> primitiveArrayListers = new HashMap<Class, Lister>();
250:
251: static {
252: // register primitive array listers
253: PrimitiveArrayListerBoolean.register();
254: PrimitiveArrayListerByte.register();
255: PrimitiveArrayListerCharacter.register();
256: PrimitiveArrayListerDouble.register();
257: PrimitiveArrayListerFloat.register();
258: PrimitiveArrayListerInteger.register();
259: PrimitiveArrayListerLong.register();
260: PrimitiveArrayListerShort.register();
261: }
262:
263: /**
264: * {@link Lister} for a collection
265: */
266: public static final class CollectionLister<BeanT, T extends Collection>
267: extends Lister<BeanT, T, Object, T> {
268:
269: /**
270: * Sometimes we need to create a new instance of a collection.
271: * This is such an implementation class.
272: */
273: private final Class<? extends T> implClass;
274:
275: public CollectionLister(Class<? extends T> implClass) {
276: this .implClass = implClass;
277: }
278:
279: public ListIterator iterator(T collection, XMLSerializer context) {
280: final Iterator itr = collection.iterator();
281: return new ListIterator() {
282: public boolean hasNext() {
283: return itr.hasNext();
284: }
285:
286: public Object next() {
287: return itr.next();
288: }
289: };
290: }
291:
292: public T startPacking(BeanT bean, Accessor<BeanT, T> acc)
293: throws AccessorException {
294: T collection = acc.get(bean);
295: if (collection == null) {
296: collection = ClassFactory.create(implClass);
297: }
298: collection.clear();
299: return collection;
300: }
301:
302: public void addToPack(T collection, Object o) {
303: collection.add(o);
304: }
305:
306: public void endPacking(T collection, BeanT bean,
307: Accessor<BeanT, T> acc) throws AccessorException {
308: // this needs to be done in the endPacking, because
309: // sometimes the accessor uses an adapter, and the adapter needs to see
310: // the whole thing
311: acc.set(bean, collection);
312: }
313:
314: public void reset(BeanT bean, Accessor<BeanT, T> acc)
315: throws AccessorException {
316: acc.get(bean).clear();
317: }
318: }
319:
320: /**
321: * {@link Lister} for IDREFS.
322: */
323: private static final class IDREFS<BeanT, PropT> extends
324: Lister<BeanT, PropT, String, IDREFS<BeanT, PropT>.Pack> {
325: private final Lister<BeanT, PropT, Object, Object> core;
326: /**
327: * Expected type to which IDREF resolves to.
328: */
329: private final Class itemType;
330:
331: public IDREFS(Lister core, Class itemType) {
332: this .core = core;
333: this .itemType = itemType;
334: }
335:
336: public ListIterator<String> iterator(PropT prop,
337: XMLSerializer context) {
338: final ListIterator i = core.iterator(prop, context);
339:
340: return new IDREFSIterator(i, context);
341: }
342:
343: public Pack startPacking(BeanT bean, Accessor<BeanT, PropT> acc) {
344: return new Pack(bean, acc);
345: }
346:
347: public void addToPack(Pack pack, String item) {
348: pack.add(item);
349: }
350:
351: public void endPacking(Pack pack, BeanT bean,
352: Accessor<BeanT, PropT> acc) {
353: }
354:
355: public void reset(BeanT bean, Accessor<BeanT, PropT> acc)
356: throws AccessorException {
357: core.reset(bean, acc);
358: }
359:
360: /**
361: * PackT for this lister.
362: */
363: private class Pack implements Patcher {
364: private final BeanT bean;
365: private final List<String> idrefs = new ArrayList<String>();
366: private final UnmarshallingContext context;
367: private final Accessor<BeanT, PropT> acc;
368:
369: public Pack(BeanT bean, Accessor<BeanT, PropT> acc) {
370: this .bean = bean;
371: this .acc = acc;
372: this .context = UnmarshallingContext.getInstance();
373: context.addPatcher(this );
374: }
375:
376: public void add(String item) {
377: idrefs.add(item);
378: }
379:
380: /**
381: * Resolves IDREFS and fill in the actual array.
382: */
383: public void run() throws SAXException {
384: try {
385: Object pack = core.startPacking(bean, acc);
386:
387: for (String id : idrefs) {
388: Callable callable = context.getObjectFromId(id,
389: itemType);
390: Object t;
391:
392: try {
393: t = (callable != null) ? callable.call()
394: : null;
395: } catch (SAXException e) {
396: throw e;
397: } catch (Exception e) {
398: throw new SAXException2(e);
399: }
400:
401: if (t == null) {
402: context.errorUnresolvedIDREF(bean, id);
403: } else {
404: TODO.prototype(); // TODO: check if the type of t is proper.
405: core.addToPack(pack, t);
406: }
407: }
408:
409: core.endPacking(pack, bean, acc);
410: } catch (AccessorException e) {
411: context.handleError(e);
412: }
413: }
414: }
415: }
416:
417: /**
418: * {@link Iterator} for IDREFS lister.
419: *
420: * <p>
421: * Only in ArrayElementProperty we need to get the actual
422: * referenced object. This is a kind of ugly way to make that work.
423: */
424: public static final class IDREFSIterator implements
425: ListIterator<String> {
426: private final ListIterator i;
427: private final XMLSerializer context;
428: private Object last;
429:
430: private IDREFSIterator(ListIterator i, XMLSerializer context) {
431: this .i = i;
432: this .context = context;
433: }
434:
435: public boolean hasNext() {
436: return i.hasNext();
437: }
438:
439: /**
440: * Returns the last referenced object (not just its ID)
441: */
442: public Object last() {
443: return last;
444: }
445:
446: public String next() throws SAXException, JAXBException {
447: last = i.next();
448: String id = context.grammar.getBeanInfo(last, true).getId(
449: last, context);
450: if (id == null) {
451: context.errorMissingId(last);
452: }
453: return id;
454: }
455: }
456:
457: /**
458: * Special {@link Lister} used to recover from an error.
459: */
460: public static final Lister ERROR = new Lister() {
461: public ListIterator iterator(Object o, XMLSerializer context) {
462: return EMPTY_ITERATOR;
463: }
464:
465: public Object startPacking(Object o, Accessor accessor) {
466: return null;
467: }
468:
469: public void addToPack(Object o, Object o1) {
470: }
471:
472: public void endPacking(Object o, Object o1, Accessor accessor) {
473: }
474:
475: public void reset(Object o, Accessor accessor) {
476: }
477: };
478:
479: private static final ListIterator EMPTY_ITERATOR = new ListIterator() {
480: public boolean hasNext() {
481: return false;
482: }
483:
484: public Object next() {
485: throw new IllegalStateException();
486: }
487: };
488:
489: }
|