📜 ⬆️ ⬇️

Jerminal - terminal emulator for java programs

Introduction


Hi, habrayuzer! I decided to tell you about the Jerminal mini-library. I am currently working on a large commercial project on Groovy / Java. Well, I received a task - to write a console for the application. Unfortunately, the condition was set: no third-party solutions, everything is only his own. Without thinking, I sat down and wrote it. Read more - under the cut.


Goals


In principle, something very serious was not demanded of me. Here are the parameters:



In general, a fairly simple list. For those who can't wait to see the result, I give a link to the repository: kkremen / jerminal .


Code review


Well, now to the point. After reading a little about Reflection and lambda, I decided to make a "knight's move."
First of all, I created the Executable interface:


Executable.java
 package org.meinkopf.console; import java.lang.reflect.InvocationTargetException; public interface Executable { Object invoke(Object[] args) throws InvocationTargetException, IllegalAccessException; } 

If anyone does not know, you can do this:


 Executable ex = (args) -> { return null; } ex.invoke(someArgs); 

Now when ex.invoke(...) called, the lambda expression will be executed. Well, to support Reflection, I created the BasicExecutable class that inherits Executable :


BasicExecutable.java
 package org.meinkopf.console; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Arrays; @SuppressWarnings({ "WeakerAccess", "unused" }) public class BasicExecutable implements Executable { protected Method method; protected Object target; public BasicExecutable(Method method, Object target) { this.method = method; this.target = target; } /* Getters and Setters */ public Object invoke(Object[] args) throws InvocationTargetException, IllegalAccessException { if (args.length < method.getParameterCount()) { return "Too few arguments!\n"; //    ,     } return method.invoke(target, Arrays.copyOfRange(args, 0, method.getParameterCount())); //    ,    } } 

I also wrote a pair of interface-class for the list of commands: CommandList and BasicCommandList respectively. The base class stores commands in Map < String, Executable > and returns objects of type Executable main class.


Hidden text
 package org.meinkopf.console; public interface CommandList { Executable get(String commandName); } 

Hidden text
 package org.meinkopf.console; import java.util.HashMap; import java.util.Map; @SuppressWarnings({ "unused", "WeakerAccess" }) public class BasicCommandList implements CommandList { protected Map < String, Executable > methodMap = new HashMap <>(); @Override public Executable get(String command) { return methodMap.get(command); } public void register(@SuppressWarnings("SameParameterValue") String name, Executable command) { methodMap.put(name, command); } } 

The service class Command stores the command parsed by the parser as a string-name and an ArrayList arguments. Arguments, by the way, only string are accepted, that is, numbers are also transmitted as strings.


The main class implements Runnable , but so far I have not used it at all, and I call the run method directly.


Hidden text
 package org.meinkopf.console; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.Arrays; import java.util.Scanner; @SuppressWarnings({ "WeakerAccess", "unused", "SpellCheckingInspection" }) public class Jerminal implements Runnable { public static String PROMPT = " ~ $ "; public static String HEADER = "Jerminal v0.0.1 for JVM Apps\nCopyright: Karl Meinkopf, 2017\n"; protected ArrayList < Command > commandHistory = new ArrayList <>(); protected CommandList commandList = null; private Scanner scanner; public JConsole(CommandList list) { commandList = list; scanner = new Scanner(System.in); } protected Command parse(String rawCommand) { rawCommand = rawCommand.replaceAll("(\"[\\s\\S]*?\"|[\\S]+)\\s*", "$1\u0001"); rawCommand = rawCommand.replaceAll("\"([\\s\\S]*?)\"", "$1"); String[] rawList = rawCommand.split("\u0001"); ArrayList < String > args = new ArrayList <>(Arrays.asList(rawList)); String command = args.remove(0).trim(); return new Command(command, rawCommand, args); } protected Object execute(Command command) throws InvocationTargetException, IllegalAccessException { Executable executable = commandList.get(command.getName()); if (executable == null) { return "Can't find command: '" + command.getName() + "'"; } return executable.invoke(command.getArgs().toArray()); } protected void prompt( ) { System.err.println(); System.err.print(PROMPT); Command command = parse(getInputLine()); Object result; try { result = execute(command); } catch (InvocationTargetException | IllegalAccessException e) { System.err.println("Can not execute command '" + command.getName() + "'!\n\tReason: " + e.getMessage()); return; } if (result != null) System.err.println(result.toString()); } protected String getInputLine( ) { return scanner.nextLine(); } public void run( ) { System.err.println(HEADER); //noinspection InfiniteLoopStatement while (true) { prompt(); } } } 

Conclusion


Well, that's the whole library. While small, but I plan to develop and improve it. If you have comments and advice - I will be glad to hear.


')

Source: https://habr.com/ru/post/327234/


All Articles