package org.jboss.seam.remoting.wrapper;
import java.io.IOException;
import java.io.OutputStream;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import org.dom4j.Element;
/**
* @author Shane Bryzak
*/
public class MapWrapper extends BaseWrapper implements Wrapper
{
private static final byte[] MAP_TAG_OPEN = "<map>".getBytes();
private static final byte[] MAP_TAG_CLOSE = "</map>".getBytes();
private static final byte[] ELEMENT_TAG_OPEN = "<element>".getBytes();
private static final byte[] ELEMENT_TAG_CLOSE = "</element>".getBytes();
private static final byte[] KEY_TAG_OPEN = "<k>".getBytes();
private static final byte[] KEY_TAG_CLOSE = "</k>".getBytes();
private static final byte[] VALUE_TAG_OPEN = "<v>".getBytes();
private static final byte[] VALUE_TAG_CLOSE = "</v>".getBytes();
public void marshal(OutputStream out) throws IOException
{
out.write(MAP_TAG_OPEN);
Map m = (Map) this.value;
for (Object key : m.keySet())
{
out.write(ELEMENT_TAG_OPEN);
out.write(KEY_TAG_OPEN);
context.createWrapperFromObject(key, String.format("%s[key]", path)).marshal(out);
out.write(KEY_TAG_CLOSE);
out.write(VALUE_TAG_OPEN);
context.createWrapperFromObject(m.get(key), String.format("%s[value]", path)).marshal(out);
out.write(VALUE_TAG_CLOSE);
out.write(ELEMENT_TAG_CLOSE);
}
out.write(MAP_TAG_CLOSE);
}
public Object convert(Type type) throws ConversionException
{
if (context == null)
{
throw new IllegalStateException("No call context has been set");
}
Class typeClass = null;
Type keyType = null;
Type valueType = null;
// Either the type should be a generified Map
if (type instanceof ParameterizedType && Map.class.isAssignableFrom((Class) ((ParameterizedType) type).getRawType()))
{
typeClass = (Class) ((ParameterizedType) type).getRawType();
for (Type t : ((ParameterizedType) type).getActualTypeArguments())
{
if (keyType == null)
keyType = t;
else
{
valueType = t;
break;
}
}
}
// Or a non-generified Map
else if (type instanceof Class && Map.class.isAssignableFrom((Class) type))
{
if (!((Class) type).isInterface())
typeClass = (Class) type;
keyType = Object.class;
valueType = Object.class;
}
// If it's neither, throw an exception
else
throw new ConversionException(String.format("Cannot convert value to type [%s]", type));
// If we don't have a concrete type, default to creating a HashMap
if (typeClass == null || typeClass.isInterface())
value = new HashMap();
else
{
try
{
// Otherwise create an instance of the concrete type
if (type instanceof Class)
{
value = ((Class) type).newInstance();
}
else if (type instanceof ParameterizedType)
{
value = ((Class) ((ParameterizedType) type).getRawType()).newInstance();
}
}
catch (Exception ex)
{
throw new ConversionException(String.format("Could not create value of type [%s]", type));
}
}
for (Element e : (List<Element>) element.elements("element"))
{
Element keyElement = (Element) e.element("k").elementIterator().next();
Element valueElement = (Element) e.element("v").elementIterator().next();
((Map) value).put(context.createWrapperFromElement(keyElement).convert(keyType),
context.createWrapperFromElement(valueElement).convert(valueType));
}
return value;
}
public ConversionScore conversionScore(Class cls)
{
if (Map.class.isAssignableFrom(cls))
return ConversionScore.exact;
if (cls.equals(Object.class))
return ConversionScore.compatible;
return ConversionScore.nomatch;
}
}