package org.jboss.seam.remoting.wrapper;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.Array;
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 org.dom4j.Element;
import org.hibernate.collection.PersistentCollection;
/**
* Wrapper for collections, arrays, etc.
*
* @author Shane Bryzak
*/
public class BagWrapper extends BaseWrapper implements Wrapper
{
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();
public void marshal(OutputStream out) throws IOException
{
out.write(BAG_TAG_OPEN);
// Fix to prevent uninitialized lazy loading in Hibernate
if (value instanceof PersistentCollection)
{
if (!((PersistentCollection) value).wasInitialized())
{
out.write(BAG_TAG_CLOSE);
return;
}
}
Collection vals = null;
// If the value is an array, convert it to a Collection
if (value.getClass().isArray())
{
vals = new ArrayList();
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())
return ConversionScore.compatible;
if (cls.equals(Object.class))
return ConversionScore.compatible;
if (Collection.class.isAssignableFrom(cls))
return ConversionScore.compatible;
return ConversionScore.nomatch;
}
}