Mercurial Hosting > nabble
view src/nabble/naml/compiler/JavaCommand.java @ 0:7ecd1a4ef557
add content
author | Franklin Schmidt <fschmidt@gmail.com> |
---|---|
date | Thu, 21 Mar 2019 19:15:52 -0600 |
parents | |
children |
line wrap: on
line source
package nabble.naml.compiler; import fschmidt.util.java.Computable; import fschmidt.util.java.Interner; import fschmidt.util.java.Memoizer; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; import java.lang.reflect.Type; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.Map; import java.util.regex.Pattern; public final class JavaCommand implements Meaning { final String name; final Method method; final Class scopedType; final CommandSpec cmdSpec; final boolean isStatic; private final String id; private JavaCommand(String name,Method method) { this.name = name; this.method = method; this.scopedType = getScopedType(); this.cmdSpec = getCommandSpec(); this.isStatic = Modifier.isStatic(method.getModifiers()); this.id = method.getDeclaringClass().getName() + '.' + name; } @Override public String toString() { return "{JavaCommand id=" + id + " method=[" + method + "]}"; } @Override public String getName() { return name; } @Override public String getId() { return id; } @Override public Collection<String> getRequiredNamespaces() { return Collections.singleton(JavaNamespace.getNamespace(method.getDeclaringClass()).name); } public String addsNamespace() { if( scopedType == null ) return null; return JavaNamespace.getNamespace(scopedType).name; } public Method getMethod() { return method; } private Class getScopedType() { if( !ScopedInterpreter.class.isAssignableFrom(method.getParameterTypes()[1]) ) return null; ParameterizedType pt = (ParameterizedType)method.getGenericParameterTypes()[1]; return getClass(pt.getActualTypeArguments()[0]); } private static Class getClass(Type type) { if( type instanceof ParameterizedType ) { ParameterizedType pt = (ParameterizedType)type; return (Class)pt.getRawType(); } return (Class)type; } private CommandSpec getCommandSpec() { try { Field f = method.getDeclaringClass().getDeclaredField(method.getName()); // f.setAccessible(true); if( !f.getType().equals(CommandSpec.class) ) throw new RuntimeException("field "+f+" which matches "+method+" must be a CommandSpec"); if( !Modifier.isStatic(f.getModifiers()) ) throw new RuntimeException("field "+f+" which matches "+method+" must be static"); return (CommandSpec)f.get(null); } catch(IllegalAccessException e) { throw new TemplateRuntimeException(e); } catch(NoSuchFieldException e) { return CommandSpec.EMPTY; } } @Override public boolean equals(Object obj) { if( !(obj instanceof JavaCommand) ) return false; JavaCommand jc = (JavaCommand)obj; return jc.method.equals(method); } @Override public int hashCode() { return method.hashCode(); } private static final Interner<JavaCommand> interner = new Interner<JavaCommand>(); private static final Memoizer<Class,Map<String,JavaCommand>> cache = new Memoizer<Class,Map<String,JavaCommand>>(new Computable<Class,Map<String,JavaCommand>>() { public Map<String,JavaCommand> get(Class cls) { Map<String,JavaCommand> map = new HashMap<String,JavaCommand>(); for( Method m : cls.getMethods() ) { Command command = m.getAnnotation(Command.class); if( command == null ) continue; String name = command.value(); if( name.length()==0 ) name = m.getName(); if( m.getReturnType() != Void.TYPE ) throw new RuntimeException("command "+m+" doesn't return void"); Class<?>[] params = m.getParameterTypes(); if( params.length != 2 ) throw new RuntimeException("command "+m+" should have 2 params"); if( !params[0].equals(IPrintWriter.class) ) throw new RuntimeException("first param of command "+m+" should be of type IPrintWriter"); if( !Interpreter.class.isAssignableFrom(params[1]) ) throw new RuntimeException("first param of command "+m+" should be of type Interpreter"); JavaCommand jc = interner.intern(new JavaCommand(name,m)); JavaCommand old = map.put(name,jc); if( old != null ) throw new RuntimeException("duplicate commands "+old.method+" and "+m); } return map; } }); static JavaCommand getJavaCommand(Class cls,String name) { return cache.get(cls).get(name); } public String[] getParameterNames() { return getCommandSpec().getParameters().toArray(new String[0]); } public String getDotParameterName() { return getCommandSpec().dotParameter; } public String[] getRequiredParameterNames() { return getCommandSpec().requiredParameters.toArray(new String[0]); } private static final Pattern ID_PTN = Pattern.compile("([^.!]+\\.)+[^.!]+"); public static boolean isJavaCommandId(String id) { return ID_PTN.matcher(id).matches(); } }