package net.sourceforge.javautil.groovy.dsl.impl.standard;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import groovy.lang.MetaClass;
import net.sourceforge.javautil.common.CollectionUtil;
import net.sourceforge.javautil.common.ReflectionUtil;
import net.sourceforge.javautil.common.reflection.cache.ClassCache;
import net.sourceforge.javautil.common.reflection.cache.ClassDescriptor;
import net.sourceforge.javautil.common.reflection.cache.ClassMethod;
import net.sourceforge.javautil.common.reflection.cache.ClassProperty;
import net.sourceforge.javautil.groovy.dsl.GroovyDSL;
import net.sourceforge.javautil.groovy.dsl.GroovyDSLMethod;
import net.sourceforge.javautil.groovy.dsl.GroovyDSLProperty;
import org.codehaus.groovy.runtime.GroovyCategorySupport;
/**
* A groovy DSL based on the {@link GroovyCategorySupport} pattern. It also
* allows new properties via static property getter/setter pattern.
*
* @author elponderador
* @author $Author$
* @version $Id$
*/
public class GroovyDSLCategoryClass implements GroovyDSL {
protected final Class<?>[] classes;
protected final Map<String, List<ClassDescriptor>> lookup = new HashMap<String, List<ClassDescriptor>>();
protected final Map<String, GroovyDSLCategoryProperty> properties = new HashMap<String, GroovyDSLCategoryProperty>();
public GroovyDSLCategoryClass (Class<?>... categoryClasses) {
Set<Class<?>> classes = new LinkedHashSet<Class<?>>();
for (Class<?> categoryClass : categoryClasses) {
ClassDescriptor cd = ClassCache.getFor(categoryClass);
Map<String, List<ClassMethod>> methodMap = cd.getMethods();
for (String name : methodMap.keySet()) {
boolean property = name.startsWith("get") && name.length() > 3 || name.startsWith("is") && name.length() > 2;
String setterName = null;
String getterName = null;
String propertyName = null;
if (property) {
getterName = name;
setterName = name.startsWith("is") ? name.substring(2) : name.substring(3);
propertyName = setterName.length() > 1 ? setterName.substring(0, 1).toLowerCase() + setterName.substring(1) : setterName.toLowerCase();
setterName = "set" + setterName;
}
List<ClassMethod> methods = methodMap.get(name);
for (ClassMethod method : methods) {
if (method.isStatic() && method.getParameterTypes().length > 0) {
Class forType = method.getParameterTypes()[0];
this.addCategory(classes, forType, cd);
if (forType.isPrimitive() || ReflectionUtil.isBoxedType(forType))
this.addCategory(classes, forType.isPrimitive() ? ReflectionUtil.getBoxedType(forType) : ReflectionUtil.getPrimitiveType(forType), cd);
if (property && method.getParameterTypes().length == 1) {
ClassMethod setter = null;
for (ClassMethod setterp : cd.getMethods(setterName)) {
if (setterp.getParameterTypes().length == 2 && setterp.getParameterTypes()[0] == forType && setterp.getParameterTypes()[1] == method.getReturnType()) {
setter = setterp; break;
}
}
this.properties.put(forType.getName() + ":" + propertyName, new GroovyDSLCategoryProperty(method, setter));
}
}
}
}
}
this.classes = classes.toArray(new Class<?>[classes.size()]);
}
public Class<?>[] getForClasses() { return this.classes; }
public GroovyDSLMethod getMethod(MetaClass clazz, Object instance, String name, Object... arguments) {
List<ClassDescriptor> categories = this.lookup.get(instance instanceof Class ? "java.lang.Class" : clazz.getTheClass().getName());
for (int i=0; i<categories.size(); i++) {
ClassMethod method = categories.get(i).findMethod(name, CollectionUtil.insert(arguments, 0, instance));
if (method != null) return new GroovyDSLCategoryMethod(method);
}
return null;
}
public GroovyDSLProperty getProperty(MetaClass clazz, Object instance, String name) {
return this.properties.get((instance instanceof Class ? "java.lang.Class" : clazz.getTheClass().getName()) + ":" + name);
}
/**
* @param classes The set of classes this DSL provides methods/properties for
* @param forType The type to add
* @param descriptor The corresponding category descriptor
*/
protected void addCategory (Set<Class<?>> classes, Class forType, ClassDescriptor descriptor) {
classes.add(forType);
if (!this.lookup.containsKey(forType.getName())) this.lookup.put(forType.getName(), new ArrayList<ClassDescriptor>());
this.lookup.get(forType.getName()).add(descriptor);
}
}