package org.javabuilders.swing.plugin.glazedlists.handler;
import static org.javabuilders.util.Preconditions.*;
import java.lang.reflect.Field;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import org.apache.commons.lang.StringUtils;
import org.javabuilders.BuildException;
import org.javabuilders.swing.plugin.glazedlists.compiler.ClassStringBuilder;
import org.javabuilders.swing.plugin.glazedlists.compiler.CompilerUtils;
import org.javabuilders.util.BuilderUtils;
import org.javabuilders.util.PropertyUtils;
import org.javabuilders.util.Tuple2;
import ca.odell.glazedlists.EventList;
import ca.odell.glazedlists.TransformedList;
import ca.odell.glazedlists.UniqueList;
/**
* Reusable class to take care of transforming the base collection into whatever
* the widget requires
* @author Jacek Furmankiewicz
*/
public class GlazedListsUtils {
public static final String SOURCE = "source";
/**
* Transform the sources into a UniqueList if required
* @return Tuple of Event list and its type
* @throws IllegalAccessException
* @throws IllegalArgumentException
* @throws SecurityException
* @throws NoSuchFieldException
*/
@SuppressWarnings("unchecked")
public static Tuple2<EventList,Class<?>> getSource(Object caller, Map<String, Object> typeDefinition) throws BuildException {
try {
String source = (String) typeDefinition.get(SOURCE);
if (source == null) {
throw new BuildException("EventTableModel.source property must be specified: {0}", typeDefinition);
}
//is it pointing to the whole collection or just one unique column
String[] parts = StringUtils.split(source, ".");
String listName = null, columnName = null;
switch (parts.length) {
case 1:
//the whole collection
listName = parts[0];
break;
case 2:
//just one column - turn it into a unique collection
listName = parts[0];
columnName = parts[1];
break;
default:
throw new BuildException("EventTableModel.source is not in valid format: {0}", source);
}
Field field = BuilderUtils.getField(caller, listName, EventList.class);
checkNotNull(field, "EventTableModel.source property does not point to a valid instance of GlazedLists EventList: {0}",typeDefinition);
Class<?> type = BuilderUtils.getGenericsTypeFromCollectionField(field);
checkNotNull(type,"Unable to use generics to find type of object stored in source: {0}", listName);
EventList list = (EventList) field.get(caller);
if (columnName != null) {
//column name specified, wants to create unique list by that column
String getterName = PropertyUtils.getGetterName(columnName);
Class<?> fieldType = PropertyUtils.verifyGetter(type, getterName,Object.class);
Comparator c = CompilerUtils.newComparator(fieldType);
//create a transformed list to convert from the original type to the field type
TransformedList transList = transformList(list,type,fieldType,getterName);
list = new UniqueList(transList, c);
type = fieldType;
}
return new Tuple2(list,type);
} catch (Exception cause) {
throw new BuildException(cause, "Unable to obtain GlazedLists source: {0}", typeDefinition);
}
}
/**
* Creates a TransformedList that transforms a list of type A intoa list of type A.oneOfItsFields
* @param <S>
* @param <D>
* @param sourceList
* @param source
* @param destination
* @param getterName
* @return
* @throws Exception
*/
@SuppressWarnings("unchecked")
public static <S,D> TransformedList<S,D> transformList(List<S> sourceList, Class<S> source, Class<D> destination,
String getterName) throws Exception {
String name = CompilerUtils.generateClassName(ReadOnlyTransformedList.class);
ClassStringBuilder bld = new ClassStringBuilder();
//constructor
bld._("public %s(%s source) {",name, EventList.class.getName())
.___("super(source);")
._("}\n");
bld._("public Object get(int index) {")
.___("return ((%s)source.get(index)).%s();", source.getName(),getterName)
._("}");
Class<?> clazz = CompilerUtils.compile(name, bld.toString(), ReadOnlyTransformedList.class);
ReadOnlyTransformedList list = (ReadOnlyTransformedList) clazz.getConstructor(EventList.class).newInstance(sourceList);
return list;
}
}