package nebula.simpletemplate;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.concurrent.locks.ReentrantLock;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.Lists;
import com.google.common.collect.Maps;
public class CompilerContext {
static class Arg {
Class<?> clz;
Map<String, Field> fields;
boolean list;
boolean map;
Map<String, Method> properties;
}
static Map<String, Arg> bytecodeWithKnownClass = ImmutableMap.of();
private static int DEFAULT_LOCALS = getMethod(Action.class, "exec").getParameterTypes().length + 1;
static ReentrantLock lock = new ReentrantLock();
private static Arg fromClass(Class<?> clz) {
Arg arg = new Arg();
arg.clz = clz;
arg.map = Map.class.isAssignableFrom(clz);
arg.list = Collection.class.isAssignableFrom(clz);
if (!arg.map && !arg.list) {
Map<String, Method> properties = Maps.newHashMap();
for (Method m : clz.getMethods()) {
if (m.getParameterTypes().length == 0) {
String methodName = m.getName();
if (methodName.startsWith("get") && methodName.length() > 3 && Character.isUpperCase(methodName.charAt(3))) {
String name = methodName.substring(3, 4).toLowerCase();
if (methodName.length() > 4) name += methodName.substring(4);
properties.put(name, m);
} else if (methodName.startsWith("has") && methodName.length() > 3 && Character.isUpperCase(methodName.charAt(3))
&& Boolean.TYPE == m.getReturnType()) {
String name = methodName.substring(3, 4).toLowerCase();
if (methodName.length() > 4) name += methodName.substring(4);
properties.put(name, m);
} else if (methodName.startsWith("is") && methodName.length() > 2 && Character.isUpperCase(methodName.charAt(2))
&& Boolean.TYPE == m.getReturnType()) {
String name = methodName.substring(2, 3).toLowerCase();
if (methodName.length() > 3) name += methodName.substring(3);
properties.put(name, m);
}
}
}
Map<String, Field> fields = Maps.newHashMap();
for (Field m : clz.getFields()) {
if (Modifier.isPublic(m.getModifiers())) {
fields.put(m.getName(), m);
}
}
arg.properties = properties;
arg.fields = fields;
}
return arg;
}
static public Arg get(Class<?> clz) {
Arg builder = bytecodeWithKnownClass.get(clz.getName());
if (builder != null) return builder;
lock.lock();
try {
builder = bytecodeWithKnownClass.get(clz.getName());
if (builder != null) return builder;
builder = fromClass(clz);
ImmutableMap.Builder<String, Arg> mapBuilder = ImmutableMap.builder();
bytecodeWithKnownClass = mapBuilder.putAll(bytecodeWithKnownClass).put(clz.getName(), builder).build();
return builder;
} finally {
lock.unlock();
}
}
static public Arg get(String className) {
Arg builder = bytecodeWithKnownClass.get(className);
if (builder != null) return builder;
lock.lock();
try {
builder = bytecodeWithKnownClass.get(className);
if (builder != null) return builder;
builder = fromClass(Class.forName(className));
ImmutableMap.Builder<String, Arg> mapBuilder = ImmutableMap.builder();
bytecodeWithKnownClass = mapBuilder.putAll(bytecodeWithKnownClass).put(className, builder).build();
return builder;
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
} finally {
lock.unlock();
}
}
public static Field getField(Class<?> clz, String name) {
return get(clz).fields.get(name);
}
static Method getMethod(Class<?> clz, String name) {
for (Method method : clz.getMethods()) {
if (method.getName().equals(name)) {
return method;
}
}
return null;
}
public static Method getProp(Class<?> clz, String name) {
return get(clz).properties.get(name);
}
public static Method getProp(String className, String name) {
return get(className).properties.get(name);
}
final List<Arg> arges;
final CompiledST impl;
int locals;
public CompilerContext(CompiledST impl,List<Class<?>> clzes) {
this.impl = impl;
this.arges = Lists.newArrayList();
for (Class<?> clz : clzes) {
if (clz != null) {
Arg arg = get(clz);
this.arges.add(arg);
} else {
this.arges.add(null);
}
}
this.locals = DEFAULT_LOCALS;
}
public CompilerContext(CompiledST impl,Object target) {
this.impl = impl;
this.arges = Lists.newArrayList();
if (target != null) {
Arg arg = get(target.getClass());
this.arges.add(arg);
} else {
this.arges.add(null);
}
this.locals = DEFAULT_LOCALS;
}
public CompilerContext(CompiledST impl,Object... argv) {
this.impl = impl;
this.arges = Lists.newArrayList();
for (Object o : argv) {
if (o != null) {
Arg arg = get(o.getClass());
this.arges.add(arg);
} else {
this.arges.add(null);
}
}
this.locals = DEFAULT_LOCALS;
}
public CompilerContext(CompiledST impl,Object target, Object... argv) {
this.impl = impl;
this.arges = Lists.newArrayList();
if (target != null) {
Arg arg = get(target.getClass());
this.arges.add(arg);
} else {
this.arges.add(null);
}
for (Object o : argv) {
if (o != null) {
Arg arg = get(o.getClass());
this.arges.add(arg);
} else {
this.arges.add(null);
}
}
this.locals = DEFAULT_LOCALS;
}
public Arg getArg(int index) {
return arges.get(index);
}
}