package org.jboss.seam.remoting.wrapper; import java.io.IOException; import java.io.OutputStream; import java.lang.reflect.Array; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.ArrayList; import java.util.Collection; import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Queue; import java.util.Set; import javax.enterprise.inject.spi.BeanManager; import org.dom4j.Element; /** * Wrapper for collections, arrays, etc. * * @author Shane Bryzak */ public class BagWrapper extends BaseWrapper implements Wrapper { public BagWrapper(BeanManager beanManager) { super(beanManager); } private static final byte[] BAG_TAG_OPEN = "<bag>".getBytes(); private static final byte[] BAG_TAG_CLOSE = "</bag>".getBytes(); private static final byte[] ELEMENT_TAG_OPEN = "<element>".getBytes(); private static final byte[] ELEMENT_TAG_CLOSE = "</element>".getBytes(); private static final byte[] UNDEFINED_TAG = "<undefined/>".getBytes(); private boolean loadLazy = false; public void setLoadLazy(boolean loadLazy) { this.loadLazy = loadLazy; } @SuppressWarnings("unchecked") public void marshal(OutputStream out) throws IOException { // Fix to prevent uninitialized lazy loading in Hibernate if (value.getClass().getName().startsWith("org.hibernate.") && !loadLazy) { try { Class<?> cls = Class.forName("org.hibernate.Hibernate"); try { Method m = cls.getMethod("isInitialized", Object.class); if (((Boolean) m.invoke(null, value)).booleanValue() == false) { out.write(UNDEFINED_TAG); return; } } catch (NoSuchMethodException ex) { } catch (InvocationTargetException ex) { } catch (IllegalAccessException ex) { } } catch (ClassNotFoundException ex) { } } // Fix to prevent uninitialized lazy loading in EclipseLink if (value.getClass().getName().startsWith("org.eclipse") && !loadLazy) { try { Class<?> klass = BagWrapper.class.getClassLoader().loadClass("org.eclipse.persistence.indirection.IndirectContainer"); if (klass.isInstance(value)) { Method m = klass.getDeclaredMethod("isInstantiated"); if(((Boolean) m.invoke(value))) { out.write(UNDEFINED_TAG); return; } } // Following the lead of eating the exceptions, as it doesn't really affect us either way } catch (ClassNotFoundException e) { } catch (NoSuchMethodException e) { } catch (InvocationTargetException e) { } catch (IllegalAccessException e) { } } out.write(BAG_TAG_OPEN); Collection<Object> vals = null; // If the value is an array, convert it to a Collection if (value.getClass().isArray()) { vals = new ArrayList<Object>(); for (int i = 0; i < Array.getLength(value); i++) { vals.add(Array.get(value, i)); } } else if (Collection.class.isAssignableFrom(value.getClass())) { vals = (Collection) value; } else { throw new RuntimeException(String.format( "Can not marshal object as bag: [%s]", value)); } for (Object val : vals) { out.write(ELEMENT_TAG_OPEN); context.createWrapperFromObject(val, path).marshal(out); out.write(ELEMENT_TAG_CLOSE); } out.write(BAG_TAG_CLOSE); } @SuppressWarnings("unchecked") public Object convert(Type type) throws ConversionException { // First convert the elements in the bag to a List of Wrappers List<Wrapper> vals = new ArrayList<Wrapper>(); for (Element e : (List<Element>) element.elements("element")) { vals.add(context.createWrapperFromElement((Element) e.elements().get(0))); } if (type instanceof Class && ((Class<?>) type).isArray()) { Class<?> arrayType = ((Class<?>) type).getComponentType(); value = Array.newInstance(arrayType, vals.size()); // Fix this for (int i = 0; i < vals.size(); i++) { Array.set(value, i, vals.get(i).convert(arrayType)); } } else if (type instanceof Class && Collection.class.isAssignableFrom((Class<?>) type)) { try { value = getConcreteClass((Class<?>) type).newInstance(); } catch (Exception ex) { throw new ConversionException(String.format( "Could not create instance of target type [%s].", type)); } for (Wrapper w : vals) { ((Collection) value).add(w.convert(Object.class)); } } else if (type instanceof ParameterizedType && Collection.class.isAssignableFrom((Class<?>) ((ParameterizedType) type).getRawType())) { Class<?> rawType = (Class<?>) ((ParameterizedType) type).getRawType(); Type genType = Object.class; for (Type t : ((ParameterizedType) type).getActualTypeArguments()) { genType = t; break; } try { value = getConcreteClass(rawType).newInstance(); } catch (Exception ex) { throw new ConversionException(String.format( "Could not create instance of target type [%s].", rawType)); } for (Wrapper w : vals) { ((Collection) value).add(w.convert(genType)); } } return value; } private Class<?> getConcreteClass(Class<?> c) { if (c.isInterface()) { // Support Set, Queue and (by default, and as a last resort) List if (Set.class.isAssignableFrom(c)) { return HashSet.class; } else if (Queue.class.isAssignableFrom(c)) { return LinkedList.class; } else { return ArrayList.class; } } else { return c; } } /** * @param cls Class * @return ConversionScore */ public ConversionScore conversionScore(Class<?> cls) { // There's no such thing as an exact match for a bag, so we'll just look // for a compatible match if (cls.isArray() || cls.equals(Object.class) || Collection.class.isAssignableFrom(cls)) { return ConversionScore.compatible; } else { return ConversionScore.nomatch; } } }