Mercurial Hosting > nabble
diff 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 diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/nabble/naml/compiler/JavaCommand.java Thu Mar 21 19:15:52 2019 -0600 @@ -0,0 +1,155 @@ +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(); + } +}