
com.sirolf2009.husk.Husk Maven / Gradle / Ivy
package com.sirolf2009.husk;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import com.sirolf2009.husk.dsl.CLI;
import com.sirolf2009.husk.dsl.ParseException;
import com.sirolf2009.husk.dsl.SimpleNode;
public class Husk {
private List allCommands;
private Map> commands;
private Object handler;
private String name;
private String splash;
private BuiltinHandler builtinHandler;
private List inputConverters;
private List outputConverters;
private static final String defaultSplash = ""+
" ____ ____ __ \n"+
"|_ || _| [ | _\n"+
" | |__| | __ _ .--. | | / ]\n"+
" | __ | [ | | | ( (`\\] | '' <\n"+
" _| | | |_ | \\_/ |, `'.'. | |`\\ \\\n"+
"|____||____|'.__.'_/[\\__) )[__| \\_]\tA nutty shell\n"+
" ";
public Husk(Object handler) throws CommandSaveException, IllegalArgumentException, IllegalAccessException {
this(handler, "Husk");
}
public Husk(Object handler, String name) throws CommandSaveException, IllegalArgumentException, IllegalAccessException {
this(handler, name, defaultSplash);
}
public Husk(Object handler, String name, String splash) throws CommandSaveException, IllegalArgumentException, IllegalAccessException {
this(handler, name, splash, new BuiltinHandler());
}
public Husk(Object handler, String name, String splash, BuiltinHandler builtinHandler) throws CommandSaveException, IllegalArgumentException, IllegalAccessException {
this.handler = handler;
this.name = name;
this.splash = splash;
this.builtinHandler = builtinHandler;
allCommands = new ArrayList();
commands = new HashMap>();
registerMethodHandler(builtinHandler);
registerMethodHandler(handler);
inputConverters = new ArrayList();
outputConverters = new ArrayList();
}
public String getSplash() {
return splash;
}
public void setSplash(String splash) {
this.splash = splash;
}
public List getInputConverters() {
return inputConverters;
}
public void setInputConverters(List inputConverters) {
this.inputConverters = inputConverters;
}
public List getOutputConverters() {
return outputConverters;
}
public void setOutputConverters(List outputConverters) {
this.outputConverters = outputConverters;
}
public void commandLoop() {
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
if(splash != null && !splash.isEmpty()) {
System.out.println(splash);
}
loop: while(true) {
try {
System.out.print(name+"> ");
CommandResults result = execute(reader.readLine());
if(result.getMethod().getMethod() != null && !result.getMethod().getMethod().getReturnType().equals(void.class)) {
for(OutputConverter outputConverter : outputConverters) {
String returned = outputConverter.convert(result.getReturnValue());
if(returned != null) {
System.out.println(returned);
continue loop;
}
}
System.out.println(result.getReturnValue());
}
} catch (IOException e) {
e.printStackTrace();
} catch (ParseException e) {
e.printStackTrace();
} catch (CommandNotFoundException e) {
System.err.println(e.getMessage());
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
}
public void registerMethodHandler(Object handler) throws CommandSaveException, IllegalArgumentException, IllegalAccessException {
for(Field field : handler.getClass().getDeclaredFields()) {
if(field.isAnnotationPresent(HuskReference.class)) {
if(!field.isAccessible() && field.getAnnotation(HuskReference.class).allowChangeVisibility()) {
field.setAccessible(true);
field.set(handler, this);
field.setAccessible(false);
} else {
field.set(handler, this);
}
}
}
for(Method method : handler.getClass().getMethods()) {
if(method.isAnnotationPresent(Command.class)) {
allCommands.add(new CommandMethod(null, null, method.getAnnotation(Command.class).description(), method));
}
}
for(CommandMethod method : allCommands) {
if(method.getName() == null) {
String name = method.getAnnotatedFullName();
if(name != null && !name.isEmpty()) {
trySaveCommand(name, method);
method.setName(name);
}
}
}
for(CommandMethod method : allCommands) {
if(method.getName() == null) {
trySaveCommand(method.getMethod().getName(), method);
method.setName(method.getMethod().getName());
}
}
for(CommandMethod method : allCommands) {
if(method.getAbbrev() == null) {
String abbrev = method.getAnnotatedAbbrev();
if(abbrev != null && !abbrev.isEmpty()) {
trySaveCommand(abbrev, method);
method.setAbbrev(abbrev);
}
}
}
for(CommandMethod method : allCommands) {
if(method.getAbbrev() == null) {
int i = 0;
while(true) {
i++;
try {
String abbrev = getAbbrev(method.getMethod().getName(), i);
trySaveCommand(abbrev, method);
method.setAbbrev(abbrev);
break;
} catch(CommandSaveException e) {
} catch(IndexOutOfBoundsException e) {
break;
}
}
}
}
}
public CommandResults execute(String command) throws CommandNotFoundException, ParseException, IllegalAccessException, InvocationTargetException {
return execute(CLI.parse(command));
}
public CommandResults execute(SimpleNode simplenode) throws CommandNotFoundException, IllegalAccessException, InvocationTargetException {
if(simplenode.jjtGetNumChildren() > 0) {
CommandResults result = executeCommand((SimpleNode) simplenode.jjtGetChild(0));
for(int i = 1; i < simplenode.jjtGetNumChildren(); i++) {
result = executeCommand((SimpleNode) simplenode.jjtGetChild(i), new Object[] {result.returnValue});
}
return result;
}
return null;
}
public CommandResults executeCommand(SimpleNode simplenode) throws CommandNotFoundException, IllegalAccessException, InvocationTargetException {
return executeCommand(simplenode, getParameters(simplenode));
}
public CommandResults executeCommand(SimpleNode simplenode, Object[] parameters) throws CommandNotFoundException, IllegalAccessException, InvocationTargetException {
return executeCommand(getWord((SimpleNode) simplenode.jjtGetChild(0)), parameters);
}
public CommandResults executeCommand(String name, Object[] parameters) throws CommandNotFoundException, IllegalAccessException, InvocationTargetException {
try {
return executeCommand(name, parameters, builtinHandler);
} catch(CommandNotFoundException e) {}
return executeCommand(name, parameters, handler);
}
public CommandResults executeCommand(String name, Object[] parameters, Object handler) throws CommandNotFoundException, IllegalAccessException, InvocationTargetException {
if(commands.get(name) != null) {
for(CommandMethod method : commands.get(name)) {
if(method.getMethod().getParameterCount() == parameters.length) {
try {
Parameter[] methodParams = method.getMethod().getParameters();
Object[] convertedParameters = new Object[parameters.length];
for(int i = 0; i < parameters.length; i++) {
convertedParameters[i] = convert(parameters[i], methodParams[i].getType());
}
Object object = method.getMethod().invoke(handler, convertedParameters);
return new CommandResults(object, method);
} catch (IllegalArgumentException | ArrayStoreException e) {
}
}
};
for(CommandMethod method : commands.get(name)) {
try {
Object object = method.getMethod().invoke(handler, parameters);
return new CommandResults(object, method);
} catch (IllegalArgumentException | ArrayStoreException e) {
}
};
for(CommandMethod method : commands.get(name)) {
try {
Parameter[] methodParams = method.getMethod().getParameters();
if(methodParams.length >= 1 && methodParams[methodParams.length-1].getType().isArray()) {
Object[] arrayParameter = Arrays.copyOfRange(parameters, methodParams.length-1, parameters.length);
Object[] newParameters = Arrays.copyOf(parameters, methodParams.length);
newParameters[methodParams.length-1] = getArray(arrayParameter, methodParams[methodParams.length-1].getType().getComponentType());
Object object = method.getMethod().invoke(handler, newParameters);
return new CommandResults(object, method);
}
} catch (IllegalArgumentException | ArrayStoreException e) {
}
};
}
throw new CommandNotFoundException(name, parameters);
}
private Object convert(Object object, Class> clazz) {
for(InputConverter converter : inputConverters) {
Object returned = converter.convert(object, clazz);
if(returned != null) {
return returned;
}
}
return object;
}
private void trySaveCommand(String name, CommandMethod method) throws CommandSaveException {
CommandMethod colliding = getCollidingCommand(name, method);
if(colliding == null) {
commands.get(name).add(method);
} else {
throw new CommandSaveException(name, method, colliding);
}
}
private CommandMethod getCollidingCommand(String name, CommandMethod method) {
if(!commands.containsKey(name)) {
commands.put(name, new ArrayList());
return null;
} else {
int parameters = method.getMethod().getParameterCount();
for(CommandMethod methodExisting : commands.get(name)) {
if(methodExisting.getMethod().getParameterCount() == parameters) {
Parameter[] myParameters = method.getMethod().getParameters();
Parameter[] hisParameters = methodExisting.getMethod().getParameters();
int matching = 0;
for(int j = 0; j < parameters; j++) {
if(myParameters[j].getType().equals(hisParameters[j].getType())) {
matching++;
}
}
if(matching == parameters) {
return methodExisting;
}
}
}
return null;
}
}
private String getAbbrev(String fullName, int letters) {
String[] words = fullName.split("(?=\\p{Upper})");
StringBuilder builder = new StringBuilder();
for(String word : words) {
builder.append(word.subSequence(0, letters).toString().toLowerCase());
}
return builder.toString();
}
@SuppressWarnings("unchecked")
private T[] getArray(Object[] array, Class target) {
T[] newArray = (T[]) Array.newInstance(target, array.length);
for(int i = 0; i < array.length; i++) {
newArray[i] = (T) array[i];
}
return newArray;
}
private Object[] getParameters(SimpleNode simplenode) {
Object[] parameters = new Object[simplenode.jjtGetNumChildren()-1];
for(int i = 0; i < simplenode.jjtGetNumChildren()-1; i++) {
parameters[i] = ((SimpleNode)simplenode.jjtGetChild(i+1)).jjtGetValue();
}
return parameters;
}
private String getWord(SimpleNode simplenode) {
return (String) simplenode.jjtGetValue();
}
public List getAllCommands() {
return allCommands;
}
public void setAllCommands(List allCommands) {
this.allCommands = allCommands;
}
public Map> getCommands() {
return commands;
}
public void setCommands(Map> commands) {
this.commands = commands;
}
public Object getHandler() {
return handler;
}
public void setHandler(Object handler) {
this.handler = handler;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public BuiltinHandler getBuiltinHandler() {
return builtinHandler;
}
public void setBuiltinHandler(BuiltinHandler builtinHandler) {
this.builtinHandler = builtinHandler;
}
public static class CommandResults {
private Object returnValue;
private CommandMethod method;
public CommandResults(Object returnValue, CommandMethod method) {
this.returnValue = returnValue;
this.method = method;
}
public Object getReturnValue() {
return returnValue;
}
public void setReturnValue(Object returnValue) {
this.returnValue = returnValue;
}
public CommandMethod getMethod() {
return method;
}
public void setMethod(CommandMethod method) {
this.method = method;
}
}
public static class CommandNotFoundException extends Exception {
private static final long serialVersionUID = 2779723193855376315L;
private String name;
private Object[] params;
public CommandNotFoundException(String name, Object[] params) {
super("Could not find the command "+name+" with parameters "+Arrays.toString(params));
this.name = name;
this.params = params;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Object[] getParams() {
return params;
}
public void setParams(Object[] params) {
this.params = params;
}
}
public static class CommandSaveException extends Exception {
private static final long serialVersionUID = -7168945547567388120L;
private String name;
private CommandMethod method;
private CommandMethod collider;
public CommandSaveException(String name, CommandMethod method, CommandMethod collider) {
super(name+" for method "+method+" is already in use by "+collider);
this.name = name;
this.method = method;
this.collider = collider;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public CommandMethod getMethod() {
return method;
}
public void setMethod(CommandMethod method) {
this.method = method;
}
public CommandMethod getCollider() {
return collider;
}
public void setCollider(CommandMethod collider) {
this.collider = collider;
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy