├── libs ├── JNativeHook.jar ├── Jama-1.0.3.jar ├── argo-3.10.jar └── jlfgr-1_0.jar ├── src ├── staticResources │ ├── Repeat.jpg │ ├── CSharpResources.java │ ├── AbstractNativeBootstrapResource.java │ ├── PythonResources.java │ ├── MouseGestureModelResources.java │ └── BootStrapResources.java ├── natives │ ├── csharp │ │ └── bin │ │ │ ├── Repeat.exe │ │ │ ├── log4net.dll │ │ │ └── Newtonsoft.Json.dll │ ├── python │ │ ├── specifications.py │ │ ├── system_host_request.py │ │ ├── system_client_request.py │ │ ├── template_repeat.py │ │ ├── keyboard_request.py │ │ ├── tool_request.py │ │ ├── request_generator.py │ │ ├── shared_memory_request.py │ │ └── mouse_request.py │ └── java │ │ └── TemplateRepeat ├── utilities │ ├── IJsonable.java │ ├── ILoggable.java │ ├── Pair.java │ ├── OSIdentifier.java │ ├── Trio.java │ ├── logging │ │ ├── ExceptionUtility.java │ │ └── OutStream.java │ ├── RandomUtil.java │ ├── IterableUtility.java │ ├── ExceptableFunction.java │ ├── swing │ │ ├── TextAreaHandler.java │ │ └── LinedTextArea.java │ ├── GeneralUtility.java │ ├── DateUtility.java │ ├── SubprocessUttility.java │ ├── Function.java │ ├── NumberUtility.java │ └── ZipUtility.java ├── frontEnd │ ├── BlankClass.java │ ├── LogPopupMenu.java │ ├── MinimizedFrame.java │ ├── IpcBackendHolder.java │ ├── IpcFrame.java │ └── Main.java ├── cli │ ├── ParseState.java │ ├── TerminalState.java │ ├── StringState.java │ ├── ChoiceState.java │ └── NonNegativeIntegerState.java ├── globalListener │ ├── GlobalListener.java │ ├── GlobalKeyListener.java │ └── GlobalMouseListener.java ├── mouseGestureModel │ ├── labels │ └── intercepts ├── core │ ├── languageHandler │ │ ├── compiler │ │ │ ├── DynamicCompilerOutput.java │ │ │ ├── AbstractNativeCompiler.java │ │ │ ├── DynamicCompilerManager.java │ │ │ ├── CSharpRemoteCompiler.java │ │ │ ├── AbstractRemoteNativeCompiler.java │ │ │ └── PythonRemoteCompiler.java │ │ ├── Language.java │ │ ├── API │ │ │ ├── PythonAPI.txt │ │ │ ├── CSharpAPI.txt │ │ │ └── JavaAPI.txt │ │ └── sourceGenerator │ │ │ ├── TaskSourceScheduler.java │ │ │ ├── AbstractSourceGenerator.java │ │ │ ├── PythonSourceGenerator.java │ │ │ ├── CSharpSourceGenerator.java │ │ │ └── JavaSourceGenerator.java │ ├── recorder │ │ ├── Task.java │ │ └── TaskScheduler.java │ ├── scheduler │ │ ├── SchedulingData.java │ │ └── AbstractScheduler.java │ ├── ipc │ │ ├── IPCServiceName.java │ │ ├── repeatServer │ │ │ ├── processors │ │ │ │ ├── IpcMessageType.java │ │ │ │ ├── TaskProcessorManager.java │ │ │ │ ├── AbstractMessageProcessor.java │ │ │ │ ├── SharedMemoryProcessor.java │ │ │ │ ├── SystemRequestProcessor.java │ │ │ │ └── ServerMainProcessor.java │ │ │ ├── ClientTask.java │ │ │ ├── MainMessageSender.java │ │ │ └── ControllerServer.java │ │ ├── repeatClient │ │ │ ├── PythonIPCClientService.java │ │ │ ├── CSharpIPCClientService.java │ │ │ └── IPCClientService.java │ │ ├── IIPCService.java │ │ └── IPCServiceManager.java │ ├── keyChain │ │ ├── mouseGestureRecognition │ │ │ ├── DataCutReverseTrimmer.java │ │ │ ├── DataCutTrimmer.java │ │ │ ├── AbstractDataTrimmer.java │ │ │ ├── DataDefinitionTrimmer.java │ │ │ ├── DataNormalizer.java │ │ │ ├── MouseGestureClassifier.java │ │ │ └── LogisticRegressionModel.java │ │ ├── MouseGesture.java │ │ └── KeyChain.java │ ├── config │ │ ├── Parser1_0.java │ │ ├── Parser1_6.java │ │ ├── Parser1_9.java │ │ ├── Parser1_5.java │ │ ├── Parser1_8.java │ │ ├── Parser1_4.java │ │ ├── Parser1_7.java │ │ ├── Parser1_3.java │ │ ├── Parser1_1.java │ │ ├── Parser1_2.java │ │ └── ConfigParser.java │ ├── userDefinedTask │ │ ├── TaskSourceManager.java │ │ ├── DormantUserDefinedTask.java │ │ ├── Tools.java │ │ ├── UsageStatistics.java │ │ ├── TaskGroup.java │ │ └── SharedVariables.java │ └── controller │ │ └── Core.java └── commonTools │ ├── ClickerTool.java │ ├── AreaClickerTool.java │ └── RepeatTool.java ├── .gitignore └── License.md /libs/JNativeHook.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hptruong93/Repeat/HEAD/libs/JNativeHook.jar -------------------------------------------------------------------------------- /libs/Jama-1.0.3.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hptruong93/Repeat/HEAD/libs/Jama-1.0.3.jar -------------------------------------------------------------------------------- /libs/argo-3.10.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hptruong93/Repeat/HEAD/libs/argo-3.10.jar -------------------------------------------------------------------------------- /libs/jlfgr-1_0.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hptruong93/Repeat/HEAD/libs/jlfgr-1_0.jar -------------------------------------------------------------------------------- /src/staticResources/Repeat.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hptruong93/Repeat/HEAD/src/staticResources/Repeat.jpg -------------------------------------------------------------------------------- /src/natives/csharp/bin/Repeat.exe: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hptruong93/Repeat/HEAD/src/natives/csharp/bin/Repeat.exe -------------------------------------------------------------------------------- /src/natives/csharp/bin/log4net.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hptruong93/Repeat/HEAD/src/natives/csharp/bin/log4net.dll -------------------------------------------------------------------------------- /src/natives/csharp/bin/Newtonsoft.Json.dll: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/hptruong93/Repeat/HEAD/src/natives/csharp/bin/Newtonsoft.Json.dll -------------------------------------------------------------------------------- /src/utilities/IJsonable.java: -------------------------------------------------------------------------------- 1 | package utilities; 2 | 3 | import argo.jdom.JsonRootNode; 4 | 5 | public interface IJsonable { 6 | public JsonRootNode jsonize(); 7 | } -------------------------------------------------------------------------------- /src/utilities/ILoggable.java: -------------------------------------------------------------------------------- 1 | package utilities; 2 | 3 | import java.util.logging.Logger; 4 | 5 | public interface ILoggable { 6 | public Logger getLogger(); 7 | } 8 | -------------------------------------------------------------------------------- /src/frontEnd/BlankClass.java: -------------------------------------------------------------------------------- 1 | package frontEnd; 2 | 3 | 4 | 5 | 6 | 7 | public class BlankClass { 8 | public static void main(String[] args) { 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /src/cli/ParseState.java: -------------------------------------------------------------------------------- 1 | package cli; 2 | 3 | import java.util.List; 4 | 5 | public abstract class ParseState { 6 | protected abstract ParseState parse(String arg, List parsed); 7 | } 8 | -------------------------------------------------------------------------------- /src/globalListener/GlobalListener.java: -------------------------------------------------------------------------------- 1 | package globalListener; 2 | 3 | public interface GlobalListener { 4 | public boolean startListening(); 5 | 6 | public boolean stopListening(); 7 | } 8 | -------------------------------------------------------------------------------- /src/mouseGestureModel/labels: -------------------------------------------------------------------------------- 1 | alpha 2 | circle_left 3 | gamma 4 | greater_than 5 | hat 6 | horizontal 7 | less_than 8 | N 9 | random 10 | six 11 | square 12 | square_root 13 | tilda 14 | triangle 15 | u 16 | vertical 17 | z -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.class 2 | 3 | # Mobile Tools for Java (J2ME) 4 | .mtj.tmp/ 5 | 6 | # Package Files # 7 | #*.jar 8 | *.war 9 | *.ear 10 | 11 | 12 | #Python Files 13 | *.pyc 14 | 15 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 16 | hs_err_pid* 17 | /bin 18 | -------------------------------------------------------------------------------- /src/utilities/Pair.java: -------------------------------------------------------------------------------- 1 | package utilities; 2 | 3 | public class Pair { 4 | 5 | private final A a; 6 | private final B b; 7 | 8 | public Pair(A a, B b) { 9 | this.a = a; 10 | this.b = b; 11 | } 12 | 13 | public A getA() { 14 | return a; 15 | } 16 | 17 | public B getB() { 18 | return b; 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /src/cli/TerminalState.java: -------------------------------------------------------------------------------- 1 | package cli; 2 | 3 | import java.util.List; 4 | 5 | public abstract class TerminalState extends ParseState { 6 | 7 | protected abstract boolean execute(List parsed); 8 | 9 | @Override 10 | protected ParseState parse(String arg, List parsed) { 11 | return null; 12 | } 13 | 14 | } 15 | -------------------------------------------------------------------------------- /src/core/languageHandler/compiler/DynamicCompilerOutput.java: -------------------------------------------------------------------------------- 1 | package core.languageHandler.compiler; 2 | 3 | public enum DynamicCompilerOutput { 4 | COMPILATION_SUCCESS, 5 | SOURCE_NOT_ACCESSIBLE, // e.g. Permission denied 6 | SOURCE_MISSING_PREFORMAT_ELEMENTS, 7 | COMPILER_MISSING, 8 | COMPILER_MISCONFIGURED, 9 | COMPILATION_ERROR, 10 | } 11 | -------------------------------------------------------------------------------- /src/core/recorder/Task.java: -------------------------------------------------------------------------------- 1 | package core.recorder; 2 | 3 | 4 | public class Task { 5 | protected static final Task EARLY_TASK = new Task(Long.MIN_VALUE, null); 6 | protected final long time; 7 | protected final Runnable task; 8 | 9 | protected Task(long time, Runnable task) { 10 | this.time = time; 11 | this.task = task; 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /src/natives/python/specifications.py: -------------------------------------------------------------------------------- 1 | 2 | 3 | SUCCESS = 'Success' 4 | FAILURE = 'Failure' 5 | 6 | server_specifications = { 7 | 'action': { 8 | 'create_task': { 9 | 'params_type' : [basestring] 10 | }, 11 | 'run_task': { 12 | 'params_type' : [int, list] 13 | }, 14 | 'remove_task': { 15 | 'params_type' : [int] 16 | } 17 | } 18 | } -------------------------------------------------------------------------------- /src/utilities/OSIdentifier.java: -------------------------------------------------------------------------------- 1 | package utilities; 2 | 3 | public class OSIdentifier { 4 | 5 | public static final String OS_NAME; 6 | public static final boolean IS_WINDOWS, IS_UNIX; 7 | 8 | static { 9 | OS_NAME = System.getProperty("os.name"); 10 | IS_WINDOWS = OS_NAME.startsWith("Windows"); 11 | IS_UNIX = !IS_WINDOWS; 12 | } 13 | 14 | private OSIdentifier() {} 15 | } 16 | -------------------------------------------------------------------------------- /src/core/scheduler/SchedulingData.java: -------------------------------------------------------------------------------- 1 | package core.scheduler; 2 | 3 | public class SchedulingData { 4 | private long time; 5 | private T data; 6 | 7 | public SchedulingData(long time, T data) { 8 | this.time = time; 9 | this.data = data; 10 | } 11 | 12 | public long getTime() { 13 | return time; 14 | } 15 | 16 | public T getData() { 17 | return data; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /src/cli/StringState.java: -------------------------------------------------------------------------------- 1 | package cli; 2 | 3 | import java.util.List; 4 | 5 | public class StringState extends ParseState { 6 | 7 | private final ParseState next; 8 | 9 | protected StringState(ParseState next) { 10 | this.next = next; 11 | } 12 | 13 | @Override 14 | protected ParseState parse(String arg, List parsed) { 15 | parsed.add(arg); 16 | return next; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/utilities/Trio.java: -------------------------------------------------------------------------------- 1 | package utilities; 2 | 3 | public class Trio { 4 | 5 | private final A a; 6 | private final B b; 7 | private final C c; 8 | 9 | public Trio(A a, B b, C c) { 10 | this.a = a; 11 | this.b = b; 12 | this.c = c; 13 | } 14 | 15 | public A getA() { 16 | return a; 17 | } 18 | 19 | public B getB() { 20 | return b; 21 | } 22 | 23 | public C getC() { 24 | return c; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/natives/python/system_host_request.py: -------------------------------------------------------------------------------- 1 | import request_generator 2 | 3 | 4 | class SystemHostRequest(request_generator.RequestGenerator): 5 | def __init__(self, client): 6 | super(SystemHostRequest, self).__init__(client) 7 | self.type = 'system_host' 8 | self.device = 'system' 9 | 10 | def keep_alive(self): 11 | self.action = 'keep_alive' 12 | self.params = [] 13 | return self.send_request(blocking_wait = False) -------------------------------------------------------------------------------- /src/utilities/logging/ExceptionUtility.java: -------------------------------------------------------------------------------- 1 | package utilities.logging; 2 | 3 | import java.io.PrintWriter; 4 | import java.io.StringWriter; 5 | import java.io.Writer; 6 | 7 | public final class ExceptionUtility { 8 | 9 | public static String getStackTrace(Exception e) { 10 | final Writer result = new StringWriter(); 11 | final PrintWriter printWriter = new PrintWriter(result); 12 | e.printStackTrace(printWriter); 13 | return result.toString(); 14 | } 15 | 16 | private ExceptionUtility(){} 17 | } 18 | -------------------------------------------------------------------------------- /src/natives/python/system_client_request.py: -------------------------------------------------------------------------------- 1 | import request_generator 2 | 3 | 4 | class SystemClientRequest(request_generator.RequestGenerator): 5 | def __init__(self, client): 6 | super(SystemClientRequest, self).__init__(client) 7 | self.type = 'system_client' 8 | self.device = 'system' 9 | 10 | def identify(self): 11 | self.action = 'identify' 12 | self.params = ['python', str(self.client.socket.getsockname()[1])] 13 | return self.send_request(blocking_wait = False) -------------------------------------------------------------------------------- /src/utilities/RandomUtil.java: -------------------------------------------------------------------------------- 1 | package utilities; 2 | 3 | public class RandomUtil { 4 | 5 | /** 6 | * Generate a random ID. 7 | * It depends on the application nature and purpose that different strategies can be used. 8 | * E.g. single threaded --> current milliseconds since epoch 9 | * UUID and hash can also be feasible in certain cases 10 | * @return a String represents the random ID 11 | */ 12 | public static String randomID() { 13 | return System.currentTimeMillis() + ""; 14 | } 15 | 16 | private RandomUtil() {} 17 | } 18 | -------------------------------------------------------------------------------- /src/cli/ChoiceState.java: -------------------------------------------------------------------------------- 1 | package cli; 2 | 3 | import java.util.HashMap; 4 | import java.util.List; 5 | 6 | public class ChoiceState extends ParseState { 7 | 8 | private final HashMap choices; 9 | 10 | protected ChoiceState(HashMap choices) { 11 | this.choices = choices; 12 | } 13 | 14 | @Override 15 | protected ParseState parse(String arg, List parsed) { 16 | if (choices.containsKey(arg)) { 17 | parsed.add(arg); 18 | return choices.get(arg); 19 | } else { 20 | return null; 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/natives/python/template_repeat.py: -------------------------------------------------------------------------------- 1 | from key_code import * 2 | 3 | """ 4 | Refer to API to see how to use repeat_api 5 | invoker is the hotkey (list of keys) that invoked this action 6 | """ 7 | def action(controller, invoker): 8 | mem = controller.shared_memory.get_instance('global') # Change string to change namespace 9 | m = controller.mouse 10 | k = controller.key 11 | t = controller.tool 12 | 13 | keys = [] if len(invoker['hotkey']) == 0 else invoker['hotkey'][0] 14 | gesture = None if len(invoker['mouse_gesture']) == 0 else invoker['mouse_gesture'][0]['name'] 15 | #Begin generated code 16 | -------------------------------------------------------------------------------- /src/utilities/IterableUtility.java: -------------------------------------------------------------------------------- 1 | package utilities; 2 | 3 | import java.util.List; 4 | import java.util.ListIterator; 5 | 6 | public class IterableUtility { 7 | 8 | public static int[] toIntegerArray(List list) { 9 | int[] output = new int[list.size()]; 10 | 11 | for (ListIterator iterator = list.listIterator(); iterator.hasNext();) { 12 | int i = iterator.nextIndex(); 13 | Object o = iterator.next(); 14 | output[i] = (int) o; 15 | } 16 | return output; 17 | } 18 | 19 | /** 20 | * Private constructor so that no instance is created 21 | */ 22 | private IterableUtility() {}; 23 | } 24 | -------------------------------------------------------------------------------- /src/cli/NonNegativeIntegerState.java: -------------------------------------------------------------------------------- 1 | package cli; 2 | 3 | import java.util.List; 4 | 5 | import utilities.NumberUtility; 6 | 7 | public class NonNegativeIntegerState extends ParseState { 8 | 9 | private final ParseState next; 10 | 11 | protected NonNegativeIntegerState(ParseState next) { 12 | this.next = next; 13 | } 14 | 15 | @Override 16 | protected ParseState parse(String arg, List parsed) { 17 | if (NumberUtility.isInteger(arg)) { 18 | int val = Integer.parseInt(arg); 19 | if (val >= 0) { 20 | parsed.add(val); 21 | return next; 22 | } else { 23 | } 24 | } 25 | return null; 26 | } 27 | 28 | } 29 | -------------------------------------------------------------------------------- /src/core/ipc/IPCServiceName.java: -------------------------------------------------------------------------------- 1 | package core.ipc; 2 | 3 | public enum IPCServiceName { 4 | CONTROLLER_SERVER(0), 5 | PYTHON(1), 6 | CSHARP(2), 7 | ; 8 | 9 | public static IPCServiceName[] ALL_SERVICE_NAMES = new IPCServiceName[] {CONTROLLER_SERVER, PYTHON, CSHARP}; 10 | private final int index; 11 | 12 | /** 13 | * @param index 14 | */ 15 | private IPCServiceName(final int index) { 16 | this.index = index; 17 | } 18 | 19 | protected int value() { 20 | return index; 21 | } 22 | 23 | @Override 24 | public String toString() { 25 | return index + ""; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/mouseGestureModel/intercepts: -------------------------------------------------------------------------------- 1 | 11000001110110100110101101010000 2 | 01000000111000001110111110000011 3 | 00111111001110010000100111110010 4 | 11000000110000010000100100000001 5 | 11000001100101110010010000100110 6 | 11000001110101100100100000000001 7 | 11000001110001000000111001110100 8 | 11000000011110000000010100011110 9 | 01000000101110001111110100010110 10 | 11000001000010001010110001100000 11 | 11000000001010100000110101000110 12 | 00111111101011000001001010111100 13 | 11000010001001010000001000100000 14 | 11000001100110100110110011011001 15 | 11000001100010100011001000000000 16 | 11000001111011111101110010110100 17 | 11000001010111000110110110110110 -------------------------------------------------------------------------------- /src/utilities/ExceptableFunction.java: -------------------------------------------------------------------------------- 1 | package utilities; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | 6 | public abstract class ExceptableFunction { 7 | 8 | public abstract R apply(D d) throws E; 9 | 10 | public List applyList(List ds) throws E { 11 | List output = new ArrayList(); 12 | for (D d : ds) { 13 | output.add(this.apply(d)); 14 | } 15 | return output; 16 | } 17 | 18 | public ExceptableFunction identity() { 19 | return new ExceptableFunction() { 20 | @Override 21 | public D apply(D d) { 22 | return d; 23 | } 24 | }; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /src/commonTools/ClickerTool.java: -------------------------------------------------------------------------------- 1 | package commonTools; 2 | 3 | import core.languageHandler.Language; 4 | 5 | public class ClickerTool extends RepeatTool { 6 | 7 | @Override 8 | protected boolean isSupported(Language language) { 9 | return language == Language.JAVA; 10 | } 11 | 12 | @Override 13 | protected String getBodySource(Language language) { 14 | if (language == Language.JAVA) { 15 | StringBuilder output = new StringBuilder(); 16 | output.append(TWO_TAB + "for (int i = 0; ; i++) {\n"); 17 | output.append(THREE_TAB + "controller.mouse().leftClick();\n"); 18 | output.append(TWO_TAB + "}\n"); 19 | 20 | return output.toString(); 21 | } 22 | 23 | return null; 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/core/keyChain/mouseGestureRecognition/DataCutReverseTrimmer.java: -------------------------------------------------------------------------------- 1 | package core.keyChain.mouseGestureRecognition; 2 | 3 | import java.awt.Point; 4 | import java.util.ArrayList; 5 | import java.util.Iterator; 6 | 7 | public class DataCutReverseTrimmer extends AbstractDataTrimmer { 8 | 9 | /** 10 | * Remove points from the end of the list. 11 | */ 12 | @Override 13 | public ArrayList internalTrim(ArrayList input) { 14 | ArrayList output = new ArrayList<>(DataNormalizer.POINT_COUNT); 15 | 16 | Iterator it = input.iterator(); 17 | for (int i = 0; i < DataNormalizer.POINT_COUNT; i++) { 18 | output.add(it.next()); 19 | } 20 | 21 | return output; 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /src/core/languageHandler/Language.java: -------------------------------------------------------------------------------- 1 | package core.languageHandler; 2 | 3 | public enum Language { 4 | JAVA("java"), 5 | PYTHON("python"), 6 | CSHARP("C#"), 7 | ; 8 | 9 | private final String text; 10 | 11 | /** 12 | * @param text 13 | */ 14 | private Language(final String text) { 15 | this.text = text; 16 | } 17 | 18 | @Override 19 | public String toString() { 20 | return text; 21 | } 22 | 23 | public static Language identify(String name) { 24 | for (Language language : Language.values()) { 25 | if (name.equals(language.toString())) { 26 | return language; 27 | } 28 | } 29 | return null; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /src/core/ipc/repeatServer/processors/IpcMessageType.java: -------------------------------------------------------------------------------- 1 | package core.ipc.repeatServer.processors; 2 | 3 | public enum IpcMessageType { 4 | ACTION("action"), 5 | TASK("task"), 6 | SHARED_MEMORY("shared_memory"), 7 | SYSTEM_HOST("system_host"), 8 | SYSTEM_CLIENT("system_client"); 9 | 10 | private final String value; 11 | 12 | private IpcMessageType(String value) { 13 | this.value = value; 14 | } 15 | 16 | protected String getValue() { 17 | return value; 18 | } 19 | 20 | protected static IpcMessageType identify(String value) { 21 | for (IpcMessageType type : IpcMessageType.values()) { 22 | if (type.value.equals(value)) { 23 | return type; 24 | } 25 | } 26 | 27 | return null; 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/utilities/logging/OutStream.java: -------------------------------------------------------------------------------- 1 | package utilities.logging; 2 | 3 | import java.io.IOException; 4 | import java.io.OutputStream; 5 | 6 | import javax.swing.JTextArea; 7 | 8 | /** 9 | * This class extends from OutputStream to redirect output to a JTextArrea 10 | * @author www.codejava.net 11 | * 12 | */ 13 | public class OutStream extends OutputStream { 14 | private final JTextArea textArea; 15 | 16 | public OutStream(JTextArea textArea) { 17 | this.textArea = textArea; 18 | } 19 | 20 | @Override 21 | public void write(int b) throws IOException { 22 | textArea.append(String.valueOf((char)b)); 23 | textArea.setCaretPosition(textArea.getDocument().getLength()); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/natives/python/keyboard_request.py: -------------------------------------------------------------------------------- 1 | import request_generator 2 | 3 | 4 | class KeyboardRequest(request_generator.RequestGenerator): 5 | def __init__(self, client): 6 | super(KeyboardRequest, self).__init__(client) 7 | self.type = 'action' 8 | self.device = 'keyboard' 9 | 10 | def type_key(self, *keys): 11 | self.action = 'type' 12 | self.params = keys 13 | return self.send_request() 14 | 15 | def type_string(self, *strings): 16 | self.action = 'type_string' 17 | self.params = strings 18 | return self.send_request() 19 | 20 | def combination(self, *keys): 21 | self.action = 'combination' 22 | self.params = keys 23 | return self.send_request() -------------------------------------------------------------------------------- /src/core/ipc/repeatClient/PythonIPCClientService.java: -------------------------------------------------------------------------------- 1 | package core.ipc.repeatClient; 2 | 3 | import java.util.logging.Logger; 4 | 5 | import staticResources.BootStrapResources; 6 | import core.languageHandler.Language; 7 | 8 | public class PythonIPCClientService extends IPCClientService { 9 | 10 | @Override 11 | public String getName() { 12 | return "Python IPC client"; 13 | } 14 | 15 | @Override 16 | public Logger getLogger() { 17 | return Logger.getLogger(PythonIPCClientService.class.getName()); 18 | } 19 | 20 | @Override 21 | protected String[] getLaunchCmd() { 22 | return new String[] { executingProgram.getAbsolutePath(), "-u", BootStrapResources.getBootstrapResource(Language.PYTHON).getIPCClient().getAbsolutePath() }; 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/core/keyChain/mouseGestureRecognition/DataCutTrimmer.java: -------------------------------------------------------------------------------- 1 | package core.keyChain.mouseGestureRecognition; 2 | 3 | import java.awt.Point; 4 | import java.util.ArrayList; 5 | import java.util.ListIterator; 6 | 7 | public class DataCutTrimmer extends AbstractDataTrimmer { 8 | 9 | /** 10 | * Remove points from the beginning of the list. 11 | */ 12 | @Override 13 | public ArrayList internalTrim(ArrayList input) { 14 | if (input.size() <= DataNormalizer.POINT_COUNT) { 15 | return input; 16 | } 17 | 18 | ArrayList output = new ArrayList<>(DataNormalizer.POINT_COUNT); 19 | ListIterator it = input.listIterator(input.size() - DataNormalizer.POINT_COUNT); 20 | while (it.hasNext()) { 21 | output.add(it.next()); 22 | } 23 | 24 | return output; 25 | } 26 | 27 | } 28 | -------------------------------------------------------------------------------- /src/core/config/Parser1_0.java: -------------------------------------------------------------------------------- 1 | package core.config; 2 | 3 | import java.util.logging.Logger; 4 | 5 | import argo.jdom.JsonRootNode; 6 | 7 | public class Parser1_0 extends ConfigParser { 8 | 9 | private static final Logger LOGGER = Logger.getLogger(Parser1_0.class.getName()); 10 | 11 | @Override 12 | protected String getVersion() { 13 | return "1.0"; 14 | } 15 | 16 | @Override 17 | protected String getPreviousVersion() { 18 | return null; 19 | } 20 | 21 | @Override 22 | protected JsonRootNode internalConvertFromPreviousVersion(JsonRootNode previousVersion) { 23 | return previousVersion; 24 | } 25 | 26 | @Override 27 | protected boolean internalImportData(Config config, JsonRootNode data) { 28 | LOGGER.warning("Unsupported import data at version " + getVersion()); 29 | return false; 30 | } 31 | } -------------------------------------------------------------------------------- /src/natives/python/tool_request.py: -------------------------------------------------------------------------------- 1 | import request_generator 2 | 3 | 4 | class ToolRequest(request_generator.RequestGenerator): 5 | def __init__(self, client): 6 | super(ToolRequest, self).__init__(client) 7 | self.type = 'action' 8 | self.device = 'tool' 9 | 10 | def get_clipboard(self): 11 | self.action = 'get_clipboard' 12 | self.params = [] 13 | return self.send_request() 14 | 15 | def set_clipboard(self, data): 16 | self.action = 'set_clipboard' 17 | self.params = [data] 18 | return self.send_request() 19 | 20 | def execute(self, cmd, cwd = None): 21 | self.action = 'execute' 22 | self.params = [cmd] 23 | if cwd is not None: 24 | self.params.append(cwd) 25 | 26 | return self.send_request() 27 | -------------------------------------------------------------------------------- /src/core/keyChain/mouseGestureRecognition/AbstractDataTrimmer.java: -------------------------------------------------------------------------------- 1 | package core.keyChain.mouseGestureRecognition; 2 | 3 | import java.awt.Point; 4 | import java.util.ArrayList; 5 | 6 | /** 7 | * Remove points from the list using a certain strategy. 8 | */ 9 | public abstract class AbstractDataTrimmer { 10 | 11 | /** 12 | * Remove points from the list so that the list has at most {@link DataNormalizer.POINT_COUNT} points. 13 | * 14 | * @param input input list of points. 15 | * @return a list of points with at most {@link DataNormalizer.POINT_COUNT} points. 16 | */ 17 | public final ArrayList trim(ArrayList input) { 18 | if (input.size() <= DataNormalizer.POINT_COUNT) { 19 | return input; 20 | } 21 | 22 | return internalTrim(input); 23 | } 24 | 25 | protected abstract ArrayList internalTrim(ArrayList input); 26 | } 27 | -------------------------------------------------------------------------------- /src/core/ipc/repeatClient/CSharpIPCClientService.java: -------------------------------------------------------------------------------- 1 | package core.ipc.repeatClient; 2 | 3 | import java.util.logging.Logger; 4 | 5 | import staticResources.BootStrapResources; 6 | import utilities.OSIdentifier; 7 | import core.languageHandler.Language; 8 | 9 | public class CSharpIPCClientService extends IPCClientService { 10 | 11 | public CSharpIPCClientService() { 12 | this.executingProgram = BootStrapResources.getBootstrapResource(Language.CSHARP).getIPCClient(); 13 | } 14 | 15 | @Override 16 | public String getName() { 17 | return "C# IPC client"; 18 | } 19 | 20 | @Override 21 | public Logger getLogger() { 22 | return Logger.getLogger(CSharpIPCClientService.class.getName()); 23 | } 24 | 25 | @Override 26 | protected String[] getLaunchCmd() { 27 | if (OSIdentifier.IS_WINDOWS) { 28 | return new String[] { executingProgram.getAbsolutePath() }; 29 | } else { 30 | getLogger().info("C# compiler disabled. This only runs on Windows operating system."); 31 | return null; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/core/keyChain/mouseGestureRecognition/DataDefinitionTrimmer.java: -------------------------------------------------------------------------------- 1 | package core.keyChain.mouseGestureRecognition; 2 | 3 | import java.awt.Point; 4 | import java.util.ArrayList; 5 | 6 | public class DataDefinitionTrimmer extends AbstractDataTrimmer { 7 | 8 | /** 9 | * Leaving out points once every period. 10 | */ 11 | @Override 12 | protected ArrayList internalTrim(ArrayList input) { 13 | int length = input.size(); 14 | float leaveOutPeriod = (float) length / (length - DataNormalizer.POINT_COUNT); 15 | ArrayList result = new ArrayList<>(DataNormalizer.POINT_COUNT); 16 | int previousBase = -1; 17 | 18 | for (int i = 0; i < length; i++) { 19 | if (i == 0) { 20 | previousBase = 0; 21 | result.add(input.get(0)); 22 | continue; 23 | } 24 | 25 | int newBase = (int) ((i + 1) / leaveOutPeriod); 26 | if (newBase == previousBase) { 27 | result.add(input.get(i)); 28 | } 29 | 30 | previousBase = newBase; 31 | } 32 | 33 | return new DataCutTrimmer().trim(result); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /src/core/languageHandler/API/PythonAPI.txt: -------------------------------------------------------------------------------- 1 | Repeat - Python API 2 | Updated 10th December 2015 3 | Author: HP Truong 4 | 5 | ********************************************************************************************************** 6 | Core API 7 | I) Mouse 8 | 1) left_click(x, y) : left click at a position. If neither x nor y is provided, left click at current position 9 | 2) right_click(x, y) : right click at a position. If neither x nor y is provided, right click at current position 10 | 3) move(x, y) : move mouse to a position (in pixel) 11 | 4) move_by(x, y) : move mouse by certain pixels 12 | II) Key 13 | 1) type_key(keys) : type a series of keys sequentially. (e.g. type(VK_A,VK_B, VK_C)) 14 | 2) type_string(strings) : type a series of strings sequentially (e.g. type_string("a", "bb") 15 | 3) combination(keys) : perform a combination (e.g. combination(VK_CTRL, VK_ALT, VL_DELETE) 16 | III) Shared memory 17 | 1) get(namespace, variable) 18 | 2) set(namespace, variable, value) 19 | 3) del(namespace, variable) -------------------------------------------------------------------------------- /src/staticResources/CSharpResources.java: -------------------------------------------------------------------------------- 1 | package staticResources; 2 | 3 | import java.io.File; 4 | import java.util.logging.Logger; 5 | 6 | import utilities.FileUtility; 7 | import core.languageHandler.Language; 8 | 9 | 10 | public class CSharpResources extends AbstractNativeBootstrapResource { 11 | 12 | @Override 13 | protected boolean correctExtension(String name) { 14 | return name.endsWith(".exe") || name.endsWith(".dll"); 15 | } 16 | 17 | @Override 18 | public Logger getLogger() { 19 | return Logger.getLogger(CSharpResources.class.getName()); 20 | } 21 | 22 | @Override 23 | protected File getExtractingDest() { 24 | return new File(FileUtility.joinPath("resources", "csharp")); 25 | } 26 | 27 | @Override 28 | protected Language getName() { 29 | return Language.CSHARP; 30 | } 31 | 32 | @Override 33 | protected String getRelativeSourcePath() { 34 | return "natives/csharp/bin"; 35 | } 36 | 37 | @Override 38 | protected boolean generateKeyCode() { 39 | return true; 40 | } 41 | 42 | @Override 43 | public File getIPCClient() { 44 | return new File("resources/csharp/Repeat.exe"); 45 | } 46 | } -------------------------------------------------------------------------------- /License.md: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Hoai Phuoc Truong 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in 13 | all copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 | THE SOFTWARE. 22 | -------------------------------------------------------------------------------- /src/utilities/swing/TextAreaHandler.java: -------------------------------------------------------------------------------- 1 | package utilities.swing; 2 | 3 | import java.io.PrintWriter; 4 | import java.io.StringWriter; 5 | import java.util.logging.Handler; 6 | import java.util.logging.LogRecord; 7 | 8 | import javax.swing.JTextArea; 9 | import javax.swing.SwingUtilities; 10 | 11 | public class TextAreaHandler extends Handler { 12 | 13 | private final JTextArea area; 14 | 15 | public TextAreaHandler(JTextArea area) { 16 | this.area = area; 17 | } 18 | 19 | @Override 20 | public void publish(final LogRecord record) { 21 | System.out.println("here"); 22 | SwingUtilities.invokeLater(new Runnable() { 23 | @Override 24 | public void run() { 25 | StringWriter text = new StringWriter(); 26 | PrintWriter out = new PrintWriter(text); 27 | out.println(area.getText()); 28 | out.printf(getFormatter().format(record)); 29 | area.setText(text.toString()); 30 | } 31 | 32 | }); 33 | } 34 | 35 | @Override 36 | public void flush() { 37 | 38 | } 39 | 40 | @Override 41 | public void close() throws SecurityException { 42 | 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /src/core/scheduler/AbstractScheduler.java: -------------------------------------------------------------------------------- 1 | package core.scheduler; 2 | 3 | import java.util.LinkedList; 4 | import java.util.Stack; 5 | 6 | public abstract class AbstractScheduler { 7 | protected final LinkedList> tasks; 8 | 9 | public AbstractScheduler() { 10 | this.tasks = new LinkedList<>(); 11 | } 12 | 13 | public final synchronized boolean addTask(SchedulingData task) { 14 | if (!isLegalAddTask()) { 15 | return false; 16 | } 17 | 18 | Stack> temp = new Stack<>(); 19 | while (true) { 20 | SchedulingData lastItem; 21 | if (tasks.isEmpty()) { 22 | lastItem = null; 23 | } else { 24 | lastItem = tasks.getLast(); 25 | } 26 | 27 | if (lastItem == null || lastItem.getTime() < task.getTime()) { 28 | tasks.addLast(task); 29 | while (!temp.isEmpty()) { 30 | tasks.add(temp.pop()); 31 | } 32 | return true; 33 | } else { 34 | temp.push(tasks.removeLast()); 35 | } 36 | } 37 | } 38 | 39 | protected abstract boolean isLegalAddTask(); 40 | 41 | protected SchedulingData getLast() { 42 | return tasks.getLast(); 43 | } 44 | 45 | protected SchedulingData getFirst() { 46 | return tasks.getFirst(); 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /src/frontEnd/LogPopupMenu.java: -------------------------------------------------------------------------------- 1 | package frontEnd; 2 | 3 | import java.awt.event.ActionEvent; 4 | import java.awt.event.ActionListener; 5 | 6 | import javax.swing.JMenuItem; 7 | import javax.swing.JPopupMenu; 8 | import javax.swing.JTextArea; 9 | 10 | import utilities.GeneralUtility; 11 | 12 | @SuppressWarnings("serial") 13 | public class LogPopupMenu extends JPopupMenu { 14 | protected final JMenuItem miClear, miCopy; 15 | private final JTextArea output; 16 | 17 | protected LogPopupMenu(JTextArea output) { 18 | this.output = output; 19 | miClear = new JMenuItem("Clear"); 20 | miClear.addActionListener(new ActionListener() { 21 | @Override 22 | public void actionPerformed(ActionEvent e) { 23 | clear(); 24 | } 25 | }); 26 | 27 | miCopy = new JMenuItem("Copy"); 28 | miCopy.addActionListener(new ActionListener() { 29 | @Override 30 | public void actionPerformed(ActionEvent e) { 31 | copy(); 32 | } 33 | }); 34 | 35 | add(miCopy); 36 | add(miClear); 37 | } 38 | 39 | private void clear() { 40 | output.setText(""); 41 | } 42 | 43 | private void copy() { 44 | String text = output.getSelectedText(); 45 | if (text == null) { 46 | text = output.getText(); 47 | } 48 | 49 | GeneralUtility.copyToClipboard(text); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/core/languageHandler/API/CSharpAPI.txt: -------------------------------------------------------------------------------- 1 | Repeat - CSharp API 2 | Updated 30th December 2015 3 | Author: HP Truong 4 | 5 | ********************************************************************************************************** 6 | Core API 7 | I) Mouse 8 | 1) LeftClick() : left click at current position 9 | 2) RightClick() : right click at current position 10 | 3) LeftClick(int x, int y) : left click at position (x,y) 11 | 4) RightClick(int x, int y) : right click at position (x,y) 12 | 5) Move(int x, int y) : move mouse to position (x,y) 13 | 6) MoveBy(int x, int y) : move mouse by (x,y) vector 14 | II) Key 15 | 1) DoType(params int[] keyCodes) : type a series of keys sequentially. (e.g. type(1,2, 3)) 16 | 2) DoType(params string[] strings) : type a series of strings sequentially (e.g. type_string("a", "bb") 17 | 3) Combination(params int[] keyCodes) : perform a combination (e.g. combination(VK_CTRL, VK_ALT, VL_DELETE) 18 | III) Shared Memory 19 | 1) GetVar(string namespace, string variable) : get a value for the variable in the given namespace 20 | 2) SetVar(string namespace, string variable, string value) : set a value for the variable in the given namespace 21 | 3) DelVar(string namespace, string variable) : delete a value for the variable in the given namespace -------------------------------------------------------------------------------- /src/core/userDefinedTask/TaskSourceManager.java: -------------------------------------------------------------------------------- 1 | package core.userDefinedTask; 2 | 3 | import java.io.File; 4 | 5 | import utilities.FileUtility; 6 | 7 | import com.sun.istack.internal.logging.Logger; 8 | 9 | 10 | public class TaskSourceManager { 11 | 12 | private static final Logger LOGGER = Logger.getLogger(TaskSourceManager.class); 13 | 14 | public static boolean submitTask(UserDefinedAction task, String source) { 15 | String sourceFileName = null; 16 | if (task.getSourcePath() == null || task.getSourcePath().equals("")) { 17 | LOGGER.warning("Cannot submit task. No source file found..."); 18 | return false; 19 | } else { 20 | sourceFileName = new File(task.getSourcePath()).getName(); 21 | } 22 | File sourceFile = new File(FileUtility.joinPath("data", "source", task.getCompiler().toString(), sourceFileName)); 23 | 24 | if (!FileUtility.writeToFile(source, sourceFile, false)) { 25 | LOGGER.warning("Unable to write source to file " + sourceFile.getAbsolutePath()); 26 | return false; 27 | } 28 | 29 | task.setSourcePath(FileUtility.getRelativePwdPath(sourceFile)); 30 | return true; 31 | } 32 | 33 | public static boolean removeTask(UserDefinedAction task) { 34 | File sourceFile = new File(task.getSourcePath()); 35 | return FileUtility.removeFile(sourceFile); 36 | } 37 | 38 | private TaskSourceManager() {} 39 | } 40 | -------------------------------------------------------------------------------- /src/core/ipc/IIPCService.java: -------------------------------------------------------------------------------- 1 | package core.ipc; 2 | 3 | import java.io.IOException; 4 | 5 | import utilities.ILoggable; 6 | 7 | public abstract class IIPCService implements ILoggable { 8 | 9 | protected int port; 10 | private boolean launchAtStartup; 11 | 12 | public IIPCService() { 13 | launchAtStartup = true; 14 | } 15 | 16 | public final void startRunning() throws IOException { 17 | if (!isRunning()) { 18 | start(); 19 | } else { 20 | getLogger().info("This service is already running."); 21 | } 22 | } 23 | 24 | public final void stopRunning() throws IOException { 25 | if (!isRunning()) { 26 | return; 27 | } 28 | 29 | stop(); 30 | } 31 | 32 | protected abstract void start() throws IOException; 33 | protected abstract void stop() throws IOException; 34 | 35 | public abstract boolean isRunning(); 36 | 37 | public void setPort(int newPort) { 38 | if (isRunning()) { 39 | getLogger().warning("Cannot change port while running"); 40 | return; 41 | } 42 | this.port = newPort; 43 | } 44 | 45 | public final int getPort() { 46 | return port; 47 | } 48 | 49 | public abstract String getName(); 50 | 51 | public boolean isLaunchAtStartup() { 52 | return launchAtStartup; 53 | } 54 | 55 | public void setLaunchAtStartup(boolean launchAtStartup) { 56 | this.launchAtStartup = launchAtStartup; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /src/staticResources/AbstractNativeBootstrapResource.java: -------------------------------------------------------------------------------- 1 | package staticResources; 2 | 3 | import java.io.File; 4 | import java.io.IOException; 5 | 6 | import utilities.FileUtility; 7 | import utilities.Function; 8 | import utilities.ILoggable; 9 | import core.languageHandler.Language; 10 | 11 | public abstract class AbstractNativeBootstrapResource implements ILoggable { 12 | protected final void extractResources() throws IOException { 13 | if (!FileUtility.createDirectory(getExtractingDest().getAbsolutePath())) { 14 | getLogger().warning("Failed to extract " + getName() + " resources"); 15 | return; 16 | } 17 | 18 | final String path = getRelativeSourcePath(); 19 | FileUtility.extractFromCurrentJar(path, getExtractingDest(), new Function() { 20 | @Override 21 | public Boolean apply(String name) { 22 | return correctExtension(name); 23 | } 24 | }); 25 | 26 | if (!generateKeyCode()) { 27 | getLogger().warning("Unable to generate key code"); 28 | } 29 | } 30 | 31 | protected boolean correctExtension(String name) { 32 | return name.endsWith(".exe") || name.endsWith(".dll"); 33 | } 34 | 35 | protected abstract String getRelativeSourcePath(); 36 | protected abstract File getExtractingDest(); 37 | protected abstract Language getName(); 38 | protected abstract boolean generateKeyCode(); 39 | public abstract File getIPCClient(); 40 | } 41 | -------------------------------------------------------------------------------- /src/natives/java/TemplateRepeat: -------------------------------------------------------------------------------- 1 | package core; 2 | import core.userDefinedTask.UserDefinedAction; 3 | import core.userDefinedTask.SharedVariables; 4 | import core.userDefinedTask.Tools; 5 | import core.controller.Core; 6 | import core.controller.MouseCore; 7 | import core.controller.KeyboardCore; 8 | import core.keyChain.KeyChain; 9 | import core.keyChain.MouseGesture; 10 | import java.util.List; 11 | import java.util.ArrayList; 12 | import static java.awt.event.KeyEvent.*; 13 | import static java.awt.event.InputEvent.BUTTON1_MASK; 14 | import static java.awt.event.InputEvent.BUTTON3_MASK; 15 | import utilities.swing.SwingUtil.OptionPaneUtil; 16 | import utilities.swing.SwingUtil.DialogUtil; 17 | 18 | public class CustomAction extends UserDefinedAction { 19 | public void action(final Core c) throws InterruptedException { 20 | SharedVariables v = new SharedVariables(getNamespace()); 21 | KeyboardCore k = c.keyBoard(); 22 | MouseCore m = c.mouse(); 23 | KeyChain invokingHotkey = this.invoker.getFirstHotkey(); 24 | List invoker = invokingHotkey == null ? new ArrayList() : invokingHotkey.getKeys(); 25 | MouseGesture gesture = this.invoker.getFirstMouseGesture(); 26 | /*Begin generated code*/ 27 | } 28 | 29 | @Override 30 | public String getNamespace() { 31 | return SharedVariables.GLOBAL_NAMESPACE; 32 | } 33 | } -------------------------------------------------------------------------------- /src/commonTools/AreaClickerTool.java: -------------------------------------------------------------------------------- 1 | package commonTools; 2 | 3 | import core.languageHandler.Language; 4 | 5 | public class AreaClickerTool extends RepeatTool { 6 | 7 | public AreaClickerTool() { 8 | super(); 9 | imports.add("import java.awt.Point;"); 10 | imports.add("import utilities.Function;"); 11 | imports.add("import core.Core;"); 12 | } 13 | 14 | @Override 15 | protected boolean isSupported(Language language) { 16 | return language == Language.JAVA; 17 | } 18 | 19 | @Override 20 | protected String getBodySource(Language language) { 21 | if (language == Language.JAVA) { 22 | StringBuilder output = new StringBuilder(); 23 | output.append(TWO_TAB + "Point topLeft = new Point(0,0);\n"); 24 | output.append(TWO_TAB + "Point bottomRight = new Point(20,20);\n"); 25 | output.append(TWO_TAB + "int row = 5;\n"); 26 | output.append(TWO_TAB + "int column = 5;\n"); 27 | output.append(TWO_TAB + "controller.mouse().moveArea(topLeft, bottomRight, row, column, new Function() {\n"); 28 | output.append(THREE_TAB + "public Void apply(Point r) {\n"); 29 | output.append(FOUR_TAB + "System.out.println(\"At \" + r.x + \", \" + r.y);\n"); 30 | output.append(FOUR_TAB + "return null;\n"); 31 | output.append(THREE_TAB + "}\n"); 32 | output.append(TWO_TAB + "});\n"); 33 | 34 | return output.toString(); 35 | } 36 | 37 | return null; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/core/languageHandler/compiler/AbstractNativeCompiler.java: -------------------------------------------------------------------------------- 1 | package core.languageHandler.compiler; 2 | 3 | import java.io.File; 4 | 5 | import javax.swing.JButton; 6 | import javax.swing.JFrame; 7 | 8 | import utilities.ILoggable; 9 | import utilities.Pair; 10 | import argo.jdom.JsonNode; 11 | import core.languageHandler.Language; 12 | import core.userDefinedTask.UserDefinedAction; 13 | 14 | public abstract class AbstractNativeCompiler implements ILoggable { 15 | 16 | public abstract Pair compile(String source); 17 | public abstract Pair compile(String source, File objectFile); 18 | public abstract Language getName(); 19 | public abstract String getExtension(); 20 | public abstract String getObjectExtension(); 21 | 22 | public abstract File getPath(); 23 | public abstract void setPath(File path); 24 | 25 | public abstract boolean parseCompilerSpecificArgs(JsonNode node); 26 | public abstract JsonNode getCompilerSpecificArgs(); 27 | 28 | protected abstract File getSourceFile(String compilingAction); 29 | protected abstract String getDummyPrefix(); 30 | 31 | /*******************************************************************/ 32 | /************************Swing components***************************/ 33 | /*******************************************************************/ 34 | public abstract void promptChangePath(JFrame parent); 35 | public abstract void changeCompilationButton(JButton bCompile); 36 | } 37 | -------------------------------------------------------------------------------- /src/utilities/GeneralUtility.java: -------------------------------------------------------------------------------- 1 | package utilities; 2 | 3 | import java.awt.HeadlessException; 4 | import java.awt.Toolkit; 5 | import java.awt.datatransfer.Clipboard; 6 | import java.awt.datatransfer.DataFlavor; 7 | import java.awt.datatransfer.StringSelection; 8 | import java.awt.datatransfer.UnsupportedFlavorException; 9 | import java.io.IOException; 10 | import java.util.logging.Level; 11 | import java.util.logging.Logger; 12 | 13 | 14 | public class GeneralUtility { 15 | 16 | private static final Logger LOGGER = Logger.getLogger(GeneralUtility.class.getName()); 17 | 18 | /** 19 | * Copy a string to clipboard 20 | * @param s string to be copied to clipboard 21 | */ 22 | public static void copyToClipboard(String s) { 23 | StringSelection stringSelection = new StringSelection(s); 24 | Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); 25 | clipboard.setContents(stringSelection, null); 26 | } 27 | 28 | /** 29 | * Retrieve text from clipboard 30 | * @return text from clipboard, or empty string if error occurs. 31 | */ 32 | public static String copyFromClipboard() { 33 | try { 34 | String data = (String) Toolkit.getDefaultToolkit() 35 | .getSystemClipboard().getData(DataFlavor.stringFlavor); 36 | return data; 37 | } catch (HeadlessException | UnsupportedFlavorException | IOException e) { 38 | LOGGER.log(Level.WARNING, "Error retrieving text from clipboard.", e); 39 | return ""; 40 | } 41 | } 42 | 43 | private GeneralUtility() {} 44 | } 45 | -------------------------------------------------------------------------------- /src/core/languageHandler/sourceGenerator/TaskSourceScheduler.java: -------------------------------------------------------------------------------- 1 | package core.languageHandler.sourceGenerator; 2 | 3 | import java.util.logging.Logger; 4 | 5 | import utilities.Function; 6 | import core.scheduler.AbstractScheduler; 7 | import core.scheduler.SchedulingData; 8 | 9 | class TaskSourceScheduler extends AbstractScheduler { 10 | 11 | private static final Logger LOGGER = Logger.getLogger(TaskSourceScheduler.class.getName()); 12 | 13 | private Function getSleepSource; 14 | 15 | protected TaskSourceScheduler() { 16 | super(); 17 | } 18 | 19 | protected String getSource() { 20 | if (!isLegalAddTask()) { 21 | return null; 22 | } 23 | 24 | StringBuffer output = new StringBuffer(); 25 | 26 | long time = 0; 27 | for (SchedulingData t : tasks) { 28 | long currentTime = t.getTime(); 29 | 30 | if (currentTime < time) { 31 | LOGGER.severe("Something went really bad"); 32 | System.exit(1); 33 | } 34 | 35 | output.append(getSleepSource.apply(currentTime - time)); 36 | 37 | time = currentTime; 38 | output.append(t.getData()); 39 | } 40 | 41 | return output.toString(); 42 | } 43 | 44 | @Override 45 | protected boolean isLegalAddTask() { 46 | if (getSleepSource == null) { 47 | LOGGER.severe("Unable to generate source. Function getSleepSource is null."); 48 | return false; 49 | } 50 | return true; 51 | } 52 | 53 | protected void setSleepSource(Function getSleepSource) { 54 | this.getSleepSource = getSleepSource; 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/core/ipc/repeatServer/ClientTask.java: -------------------------------------------------------------------------------- 1 | package core.ipc.repeatServer; 2 | 3 | import utilities.IJsonable; 4 | import argo.jdom.JsonNode; 5 | import argo.jdom.JsonNodeFactories; 6 | import argo.jdom.JsonRootNode; 7 | 8 | import com.sun.istack.internal.logging.Logger; 9 | 10 | /** 11 | * This class is very similar to UserDefinedAction , except that this is meant to be 12 | * for direct communication between ipc java client and native server 13 | * @author hptruong93 14 | * 15 | */ 16 | public class ClientTask implements IJsonable { 17 | private static final Logger LOGGER = Logger.getLogger(ClientTask.class); 18 | 19 | private final int id; 20 | private final String fileName; 21 | 22 | protected ClientTask(int id, String fileName) { 23 | this.id = id; 24 | this.fileName = fileName; 25 | } 26 | 27 | public int getId() { 28 | return id; 29 | } 30 | 31 | public String getFileName() { 32 | return fileName; 33 | } 34 | 35 | @Override 36 | public JsonRootNode jsonize() { 37 | return JsonNodeFactories.object( 38 | JsonNodeFactories.field("id", JsonNodeFactories.number(id)), 39 | JsonNodeFactories.field("file_name", JsonNodeFactories.string(fileName)) 40 | ); 41 | } 42 | 43 | public static ClientTask parseJSON(JsonNode node) { 44 | try { 45 | int id = Integer.parseInt(node.getNumberValue("id")); 46 | String fileName = node.getStringValue("file_name"); 47 | return new ClientTask(id, fileName); 48 | } catch (Exception e) { 49 | LOGGER.warning("Failed to parse ClientTask.", e); 50 | return null; 51 | } 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/utilities/DateUtility.java: -------------------------------------------------------------------------------- 1 | package utilities; 2 | 3 | import java.text.ParseException; 4 | import java.text.SimpleDateFormat; 5 | import java.util.Calendar; 6 | import java.util.Date; 7 | 8 | public class DateUtility { 9 | 10 | private static final SimpleDateFormat TIME_FORMAT = new SimpleDateFormat("dd/MM/yyyy H:m:s"); 11 | private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat("dd/MM/yyyy"); 12 | 13 | public static String calendarToTimeString(Calendar calendar) { 14 | if (calendar == null) { 15 | return null; 16 | } 17 | return TIME_FORMAT.format(calendar.getTime()); 18 | } 19 | 20 | public static String calendarToDateString(Calendar calendar) { 21 | if (calendar == null) { 22 | return null; 23 | } 24 | return DATE_FORMAT.format(calendar.getTime()); 25 | } 26 | 27 | /** 28 | * Convert a string date/time into the according calendar. 29 | * The format used is the formats defined as constants above. 30 | * @param string to convert 31 | * @return the according calendar object, or null if there is a parse exception occurred. 32 | */ 33 | public static Calendar stringToCalendar(String s) { 34 | Date date = null; 35 | 36 | try { 37 | date = TIME_FORMAT.parse(s); 38 | } catch (ParseException e) { 39 | try { 40 | date = DATE_FORMAT.parse(s); 41 | } catch (ParseException e1) { 42 | return null; 43 | } 44 | } 45 | 46 | if (date == null) { 47 | return null; 48 | } 49 | Calendar output = Calendar.getInstance(); 50 | output.setTime(date); 51 | return output; 52 | } 53 | 54 | private DateUtility() {} 55 | } 56 | -------------------------------------------------------------------------------- /src/commonTools/RepeatTool.java: -------------------------------------------------------------------------------- 1 | package commonTools; 2 | 3 | import java.util.LinkedList; 4 | import java.util.List; 5 | 6 | import utilities.StringUtilities; 7 | import core.languageHandler.Language; 8 | 9 | public abstract class RepeatTool { 10 | 11 | protected static final String TAB = " "; 12 | protected static final String TWO_TAB = TAB + TAB; 13 | protected static final String THREE_TAB = TWO_TAB + TAB; 14 | protected static final String FOUR_TAB = THREE_TAB + TAB; 15 | 16 | protected List imports; 17 | 18 | public RepeatTool() { 19 | imports = new LinkedList<>(); 20 | imports.add("import core.UserDefinedAction;"); 21 | imports.add("import core.controller.Core;"); 22 | } 23 | 24 | public String getSource(Language language) { 25 | if (isSupported(language)) { 26 | return "package core;\n" 27 | + StringUtilities.join(imports, "\n") + "\n\n" 28 | + getHeader(language) + getBodySource(language) + getFooter(language); 29 | } else { 30 | return ""; 31 | } 32 | } 33 | 34 | protected String getHeader(Language language) { 35 | if (language == Language.JAVA) { 36 | return "public class CustomAction extends UserDefinedAction {\n" 37 | + " public void action(final Core controller) throws InterruptedException {\n"; 38 | } else { 39 | return ""; 40 | } 41 | } 42 | 43 | protected String getFooter(Language language) { 44 | if (language == Language.JAVA) { 45 | return " }\n}"; 46 | } else { 47 | return ""; 48 | } 49 | } 50 | 51 | protected abstract boolean isSupported(Language language); 52 | protected abstract String getBodySource(Language language); 53 | } 54 | -------------------------------------------------------------------------------- /src/core/config/Parser1_6.java: -------------------------------------------------------------------------------- 1 | package core.config; 2 | 3 | import java.util.logging.Level; 4 | import java.util.logging.Logger; 5 | 6 | import utilities.JSONUtility; 7 | import argo.jdom.JsonNode; 8 | import argo.jdom.JsonNodeFactories; 9 | import argo.jdom.JsonRootNode; 10 | 11 | public class Parser1_6 extends ConfigParser { 12 | 13 | private static final Logger LOGGER = Logger.getLogger(Parser1_6.class.getName()); 14 | 15 | @Override 16 | protected String getVersion() { 17 | return "1.6"; 18 | } 19 | 20 | @Override 21 | protected String getPreviousVersion() { 22 | return "1.5"; 23 | } 24 | 25 | @Override 26 | protected JsonRootNode internalConvertFromPreviousVersion(JsonRootNode previousVersion) { 27 | try { 28 | JsonNode globalSettings = JsonNodeFactories.object( 29 | JsonNodeFactories.field("debug", 30 | JsonNodeFactories.object( 31 | JsonNodeFactories.field("level", JsonNodeFactories.string(Level.WARNING.toString())) 32 | ) 33 | ), 34 | JsonNodeFactories.field("tray_icon_enabled", JsonNodeFactories.booleanNode(true)), 35 | JsonNodeFactories.field("enabled_halt_by_key", JsonNodeFactories.booleanNode(true)) 36 | ); 37 | 38 | JsonNode newNode = JSONUtility.addChild(previousVersion, "global_settings", globalSettings); 39 | return newNode.getRootNode(); 40 | } catch (Exception e) { 41 | LOGGER.log(Level.WARNING, "Unable to convert json from previous version " + getPreviousVersion(), e); 42 | return null; 43 | } 44 | } 45 | 46 | @Override 47 | protected boolean internalImportData(Config config, JsonRootNode data) { 48 | LOGGER.warning("Unsupported import data at version " + getVersion()); 49 | return false; 50 | } 51 | } -------------------------------------------------------------------------------- /src/core/userDefinedTask/DormantUserDefinedTask.java: -------------------------------------------------------------------------------- 1 | package core.userDefinedTask; 2 | 3 | import java.util.logging.Level; 4 | 5 | import utilities.ILoggable; 6 | import utilities.Pair; 7 | import core.controller.Core; 8 | import core.languageHandler.compiler.AbstractNativeCompiler; 9 | import core.languageHandler.compiler.DynamicCompilerOutput; 10 | 11 | public final class DormantUserDefinedTask extends UserDefinedAction implements ILoggable { 12 | 13 | private final String source; 14 | 15 | public DormantUserDefinedTask(String source) { 16 | this.source = source; 17 | } 18 | 19 | @Override 20 | public final void action(Core controller) throws InterruptedException { 21 | getLogger().log(Level.WARNING, "Task " + name + "is dormant. Recompile to use it."); 22 | } 23 | 24 | @Override 25 | public String getSource() { 26 | if (source != null) { 27 | return source; 28 | } else { 29 | return super.getSource(); 30 | } 31 | } 32 | 33 | @Override 34 | public boolean isEnabled() { 35 | return false; 36 | } 37 | 38 | @Override 39 | public void setEnabled(boolean enabled) { 40 | getLogger().log(Level.WARNING, "Task " + name + "is dormant. Recompile to enable it."); 41 | } 42 | 43 | @Override 44 | public UserDefinedAction recompile(AbstractNativeCompiler compiler, boolean clean) { 45 | Pair result = compiler.compile(source); 46 | DynamicCompilerOutput compilerStatus = result.getA(); 47 | UserDefinedAction output = result.getB(); 48 | 49 | if (compilerStatus == DynamicCompilerOutput.COMPILATION_SUCCESS) { 50 | output.syncContent(this); 51 | output.compiler = compiler.getName(); 52 | return output; 53 | } else { 54 | return this; 55 | } 56 | } 57 | } 58 | -------------------------------------------------------------------------------- /src/natives/python/request_generator.py: -------------------------------------------------------------------------------- 1 | import threading 2 | 3 | class RequestGenerator(object): 4 | 5 | REQUEST_TIMEOUT = 1 #1 second timeout 6 | base_id = 1 7 | 8 | def __init__(self, client): 9 | super(RequestGenerator, self).__init__() 10 | self.client = client 11 | 12 | @classmethod 13 | def _gen_id(cls): 14 | RequestGenerator.base_id += 1 15 | return RequestGenerator.base_id 16 | 17 | def get_request(self, new_id): 18 | return { 19 | 'type' : self.type, 20 | 'id' : new_id, 21 | 'content' : { 22 | 'device': self.device, 23 | 'action': self.action, 24 | 'parameters' : self.params 25 | } 26 | } 27 | 28 | def send_request(self, blocking_wait = True): 29 | if self.client is None or self.client.synchronization_objects is None or self.client.send_queue is None: 30 | return None 31 | 32 | new_id = RequestGenerator._gen_id() 33 | 34 | event = threading.Event() 35 | events_pool = self.client.synchronization_objects 36 | 37 | events_pool[new_id] = event 38 | 39 | sending = self.get_request(new_id) 40 | self.client.send_queue.put(sending) 41 | 42 | if blocking_wait: 43 | if not event.wait(RequestGenerator.REQUEST_TIMEOUT): 44 | print "Timeout for this request id {0}".format(new_id) 45 | return None 46 | 47 | if new_id in events_pool and events_pool[new_id] is not event: 48 | returned_object = events_pool.pop(new_id) 49 | return returned_object 50 | else: 51 | return None 52 | -------------------------------------------------------------------------------- /src/natives/python/shared_memory_request.py: -------------------------------------------------------------------------------- 1 | import request_generator 2 | 3 | 4 | class SharedMemoryRequest(request_generator.RequestGenerator): 5 | def __init__(self, client): 6 | super(SharedMemoryRequest, self).__init__(client) 7 | self.type = 'shared_memory' 8 | self.device = 'shared_memory' 9 | 10 | def get(self, namespace, variable_name): 11 | self.action = 'get' 12 | self.params = [namespace, variable_name] 13 | return self.send_request() 14 | 15 | def set(self, namespace, variable_name, value): 16 | self.action = 'set' 17 | self.params = [namespace, variable_name, str(value)] 18 | return self.send_request() 19 | 20 | def delete(self, namespace, variable_name): 21 | self.action = 'del' 22 | self.params = [namespace, variable_name] 23 | return self.send_request() 24 | 25 | def get_instance(self, namespace): 26 | return SharedMemoryInstance(self, namespace) 27 | 28 | class SharedMemoryInstance(object): 29 | """ 30 | A small class to conveniently call SharedMemoryRequest class with fixed parameters. 31 | 32 | """ 33 | def __init__(self, shared_memory_request, namespace): 34 | super(SharedMemoryInstance, self).__init__() 35 | self.shared_memory_request = shared_memory_request 36 | self.namespace = namespace 37 | 38 | def get(self, variable_name): 39 | return self.shared_memory_request.get(self.namespace, variable_name) 40 | 41 | def set(self, variable_name, value): 42 | return self.shared_memory_request.set(self.namespace, variable_name, value) 43 | 44 | def delete(self, variable_name): 45 | return self.shared_memory_request.delete(self.namespace, variable_name) 46 | -------------------------------------------------------------------------------- /src/frontEnd/MinimizedFrame.java: -------------------------------------------------------------------------------- 1 | package frontEnd; 2 | 3 | import java.awt.Frame; 4 | import java.awt.Image; 5 | import java.awt.MenuItem; 6 | import java.awt.PopupMenu; 7 | import java.awt.SystemTray; 8 | import java.awt.TrayIcon; 9 | import java.awt.event.ActionEvent; 10 | import java.awt.event.ActionListener; 11 | import java.awt.event.InputEvent; 12 | import java.awt.event.MouseAdapter; 13 | import java.awt.event.MouseEvent; 14 | 15 | public class MinimizedFrame extends TrayIcon { 16 | 17 | private final MainBackEndHolder backEnd; 18 | 19 | public MinimizedFrame(Image image, final MainBackEndHolder backEnd) { 20 | super(image); 21 | 22 | this.backEnd = backEnd; 23 | 24 | PopupMenu trayPopupMenu = new PopupMenu(); 25 | 26 | MenuItem miShow = new MenuItem("Show"); 27 | miShow.addActionListener(new ActionListener() { 28 | @Override 29 | public void actionPerformed(ActionEvent e) { 30 | showMainFrame(); 31 | } 32 | }); 33 | 34 | MenuItem miClose = new MenuItem("Exit"); 35 | miClose.addActionListener(new ActionListener() { 36 | @Override 37 | public void actionPerformed(ActionEvent e) { 38 | backEnd.exit(); 39 | } 40 | }); 41 | 42 | trayPopupMenu.add(miShow); 43 | trayPopupMenu.add(miClose); 44 | 45 | setToolTip("Repeat"); 46 | setPopupMenu(trayPopupMenu); 47 | setImageAutoSize(true); 48 | 49 | addMouseListener(new MouseAdapter(){ 50 | @Override 51 | public void mouseReleased(MouseEvent e) { 52 | if (e.getModifiers() == InputEvent.BUTTON1_MASK) { 53 | showMainFrame(); 54 | } 55 | } 56 | }); 57 | } 58 | 59 | private void showMainFrame() { 60 | SystemTray.getSystemTray().remove(MinimizedFrame.this); 61 | backEnd.main.setState(Frame.NORMAL); 62 | backEnd.main.toFront(); 63 | backEnd.main.setVisible(true); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/natives/python/mouse_request.py: -------------------------------------------------------------------------------- 1 | import request_generator 2 | 3 | class MouseRequest(request_generator.RequestGenerator): 4 | def __init__(self, client): 5 | super(MouseRequest, self).__init__(client) 6 | self.type = 'action' 7 | self.device = 'mouse' 8 | 9 | def left_click(self, x = None, y = None): 10 | self.action = 'left_lick' 11 | if x is None or y is None: 12 | self.params = [] 13 | else: 14 | self.params = [x, y] 15 | 16 | return self.send_request() 17 | 18 | def right_click(self, x = None, y = None): 19 | self.action = 'right_click' 20 | if x is None or y is None: 21 | self.params = [] 22 | else: 23 | self.params = [x, y] 24 | 25 | return self.send_request() 26 | 27 | def move(self, x, y): 28 | self.action = 'move' 29 | self.params = [x,y] 30 | return self.send_request() 31 | 32 | def move_by(self, x, y): 33 | self.action = 'move_by' 34 | self.params = [x,y] 35 | return self.send_request() 36 | 37 | def drag(self, x1, y1, x2, y2): 38 | self.action = 'drag' 39 | self.params = [x1, y1, x2, y2] 40 | return self.send_request() 41 | 42 | def drag_by(self, x, y): 43 | self.action = 'drag_by' 44 | self.params = [x,y] 45 | return self.send_request() 46 | 47 | def get_position(self): 48 | self.action = 'get_position' 49 | self.params = [] 50 | return self.send_request() 51 | 52 | def get_color(self, x = None, y = None): 53 | self.action = 'get_color' 54 | if x is None or y is None: 55 | self.params = [] 56 | else: 57 | self.params = [x, y] 58 | 59 | return self.send_request() -------------------------------------------------------------------------------- /src/core/config/Parser1_9.java: -------------------------------------------------------------------------------- 1 | package core.config; 2 | 3 | import java.util.List; 4 | import java.util.logging.Level; 5 | import java.util.logging.Logger; 6 | 7 | import utilities.JSONUtility; 8 | import argo.jdom.JsonNode; 9 | import argo.jdom.JsonNodeFactories; 10 | import argo.jdom.JsonRootNode; 11 | import core.userDefinedTask.TaskGroup; 12 | 13 | public class Parser1_9 extends ConfigParser { 14 | 15 | private static final Logger LOGGER = Logger.getLogger(Parser1_9.class.getName()); 16 | 17 | @Override 18 | protected String getVersion() { 19 | return "1.9"; 20 | } 21 | 22 | @Override 23 | protected String getPreviousVersion() { 24 | return "1.8"; 25 | } 26 | 27 | @Override 28 | protected JsonRootNode internalConvertFromPreviousVersion(JsonRootNode previousVersion) { 29 | try { 30 | JsonNode globalSettings = previousVersion.getNode("global_settings"); 31 | globalSettings = JSONUtility.addChild(globalSettings, 32 | "execute_on_key_released", 33 | JsonNodeFactories.booleanNode(false)); 34 | return JSONUtility.replaceChild(previousVersion, "global_settings", globalSettings).getRootNode(); 35 | } catch (Exception e) { 36 | LOGGER.log(Level.WARNING, "Unable to convert json from previous version " + getPreviousVersion(), e); 37 | return null; 38 | } 39 | } 40 | 41 | @Override 42 | protected boolean internalImportData(Config config, JsonRootNode root) { 43 | boolean result = true; 44 | 45 | List taskGroups = config.getBackEnd().getTaskGroups(); 46 | for (JsonNode taskGroupNode : root.getArrayNode("task_groups")) { 47 | TaskGroup taskGroup = TaskGroup.parseJSON(config.getCompilerFactory(), taskGroupNode); 48 | result &= taskGroup != null; 49 | if (taskGroup != null) { 50 | taskGroups.add(taskGroup); 51 | } 52 | } 53 | return result; 54 | } 55 | } -------------------------------------------------------------------------------- /src/utilities/SubprocessUttility.java: -------------------------------------------------------------------------------- 1 | package utilities; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.File; 5 | import java.io.InputStreamReader; 6 | import java.util.logging.Level; 7 | import java.util.logging.Logger; 8 | 9 | public class SubprocessUttility { 10 | 11 | private static final Logger LOGGER = Logger.getLogger(SubprocessUttility.class.getName()); 12 | 13 | /** 14 | * Execute a command in the runtime environment 15 | * @param command The command to execute 16 | * @param cwd directory in which the command should be executed. Set null to execute in the current directory 17 | * @return stdout of the command, or empty string if there is any exception encountered. 18 | */ 19 | public static String execute(String command, File cwd) { 20 | try { 21 | StringBuffer output = new StringBuffer(); 22 | Process process = Runtime.getRuntime().exec(command, null, cwd); 23 | BufferedReader input = new BufferedReader(new InputStreamReader(process.getInputStream())); 24 | String line; 25 | 26 | while ((line = input.readLine()) != null) { 27 | String trimmed = line.trim(); 28 | if (trimmed.length() == 0) { 29 | continue; 30 | } 31 | output.append(trimmed); 32 | output.append("\n"); 33 | } 34 | 35 | process.waitFor(); 36 | return output.toString(); 37 | } catch (Exception e) { 38 | LOGGER.log(Level.WARNING, "Exception encountered while running command " + command, e); 39 | return ""; 40 | } 41 | } 42 | 43 | /** 44 | * Execute a command in the runtime environment 45 | * @param command The command to execute 46 | * @return stdout of the command, or empty string if there is any exception encountered. 47 | */ 48 | public static String execute(String command) { 49 | return execute(command, null); 50 | } 51 | 52 | private SubprocessUttility() {} 53 | } 54 | -------------------------------------------------------------------------------- /src/staticResources/PythonResources.java: -------------------------------------------------------------------------------- 1 | package staticResources; 2 | 3 | import java.awt.event.KeyEvent; 4 | import java.io.File; 5 | import java.lang.reflect.Field; 6 | import java.util.logging.Logger; 7 | 8 | import utilities.FileUtility; 9 | import core.languageHandler.Language; 10 | 11 | 12 | public class PythonResources extends AbstractNativeBootstrapResource { 13 | 14 | @Override 15 | protected boolean correctExtension(String name) { 16 | return name.endsWith(".py"); 17 | } 18 | 19 | @Override 20 | protected boolean generateKeyCode() { 21 | StringBuilder sb = new StringBuilder(); 22 | Field[] fields = KeyEvent.class.getFields(); 23 | for (Field f : fields) { 24 | String name = f.getName(); 25 | if (!name.startsWith("VK_")) { 26 | continue; 27 | } 28 | 29 | try { 30 | sb.append(name + " = " + f.getInt(KeyEvent.class)); 31 | } catch (IllegalArgumentException e) { 32 | e.printStackTrace(); 33 | } catch (IllegalAccessException e) { 34 | e.printStackTrace(); 35 | } 36 | sb.append("\n"); 37 | } 38 | 39 | String keyCodeFilePath = FileUtility.joinPath(getExtractingDest().getAbsolutePath(), "key_code.py"); 40 | return FileUtility.writeToFile(sb.toString().trim(), new File(keyCodeFilePath), false); 41 | } 42 | 43 | @Override 44 | public Logger getLogger() { 45 | return Logger.getLogger(PythonResources.class.getName()); 46 | } 47 | 48 | @Override 49 | protected String getRelativeSourcePath() { 50 | return "natives/python"; 51 | } 52 | 53 | @Override 54 | protected File getExtractingDest() { 55 | return new File(FileUtility.joinPath("resources", "python")); 56 | } 57 | 58 | @Override 59 | protected Language getName() { 60 | return Language.PYTHON; 61 | } 62 | 63 | @Override 64 | public File getIPCClient() { 65 | return new File("resources/python/repeat_lib.py"); 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /src/core/ipc/repeatServer/processors/TaskProcessorManager.java: -------------------------------------------------------------------------------- 1 | package core.ipc.repeatServer.processors; 2 | 3 | import java.util.Collections; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | import java.util.logging.Logger; 7 | 8 | import utilities.Function; 9 | import core.ipc.IPCServiceManager; 10 | import core.languageHandler.Language; 11 | 12 | public final class TaskProcessorManager { 13 | 14 | private static final Map taskManagers; 15 | private static Function callBack; 16 | 17 | static { 18 | taskManagers = Collections.synchronizedMap(new HashMap()); 19 | } 20 | 21 | public static boolean hasProcessor(Language language) { 22 | return taskManagers.containsKey(language); 23 | } 24 | 25 | public static TaskProcessor getProcessor(Language language) { 26 | return taskManagers.get(language); 27 | } 28 | 29 | public static void identifyProcessor(String language, int port, TaskProcessor processor) { 30 | final Language identified = Language.identify(language); 31 | if (identified != null && port > 0) { 32 | getLogger().info("Identified remote compiler " + language); 33 | taskManagers.put(identified, processor); 34 | IPCServiceManager.getIPCService(identified).setPort(port); 35 | 36 | if (callBack != null) { 37 | // It is necessary to call back in a separate thread to not block the receiving thread operation 38 | new Thread() { 39 | @Override 40 | public void run() { 41 | callBack.apply(identified); 42 | } 43 | }.start(); 44 | } 45 | } 46 | } 47 | 48 | public static void setProcessorIdentifyCallback(Function callBack) { 49 | TaskProcessorManager.callBack = callBack; 50 | } 51 | 52 | public static Logger getLogger() { 53 | return Logger.getLogger(TaskProcessorManager.class.getName()); 54 | } 55 | 56 | private TaskProcessorManager() {} 57 | } 58 | -------------------------------------------------------------------------------- /src/utilities/Function.java: -------------------------------------------------------------------------------- 1 | package utilities; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Collection; 5 | import java.util.List; 6 | 7 | public abstract class Function { 8 | public abstract R apply(D d); 9 | 10 | public List map(Collection ds) { 11 | List output = new ArrayList(ds.size()); 12 | for (D d : ds) { 13 | output.add(this.apply(d)); 14 | } 15 | return output; 16 | } 17 | 18 | public List map(D[] ds) { 19 | List output = new ArrayList(ds.length); 20 | for (D d : ds) { 21 | output.add(this.apply(d)); 22 | } 23 | return output; 24 | } 25 | 26 | public List mapNotNull(Collection ds) { 27 | List output = new ArrayList(ds.size()); 28 | for (D d : ds) { 29 | R r = this.apply(d); 30 | if (r != null) { 31 | output.add(r); 32 | } 33 | } 34 | return output; 35 | } 36 | 37 | public List mapNotNull(D[] ds) { 38 | List output = new ArrayList(ds.length); 39 | for (D d : ds) { 40 | R r = this.apply(d); 41 | if (r != null) { 42 | output.add(r); 43 | } 44 | } 45 | return output; 46 | } 47 | 48 | public Function identity() { 49 | return new Function() { 50 | @Override 51 | public D apply(D d) { 52 | return d; 53 | } 54 | }; 55 | } 56 | 57 | public static Function nullFunction() { 58 | return new Function() { 59 | @Override 60 | public Void apply(Void r) { 61 | return null; 62 | } 63 | }; 64 | } 65 | 66 | public static Function trueFunction() { 67 | return new Function() { 68 | @Override 69 | public Boolean apply(Void r) { 70 | return true; 71 | } 72 | }; 73 | } 74 | 75 | public static Function falseFunction() { 76 | return new Function() { 77 | @Override 78 | public Boolean apply(Void r) { 79 | return false; 80 | } 81 | }; 82 | } 83 | } 84 | -------------------------------------------------------------------------------- /src/core/config/Parser1_5.java: -------------------------------------------------------------------------------- 1 | package core.config; 2 | 3 | import java.util.logging.Level; 4 | import java.util.logging.Logger; 5 | 6 | import utilities.Function; 7 | import argo.jdom.JsonNode; 8 | import argo.jdom.JsonNodeFactories; 9 | import argo.jdom.JsonRootNode; 10 | 11 | public class Parser1_5 extends ConfigParser { 12 | 13 | private static final Logger LOGGER = Logger.getLogger(Parser1_5.class.getName()); 14 | 15 | @Override 16 | protected String getVersion() { 17 | return "1.5"; 18 | } 19 | 20 | @Override 21 | protected String getPreviousVersion() { 22 | return "1.4"; 23 | } 24 | 25 | @Override 26 | protected JsonRootNode internalConvertFromPreviousVersion(JsonRootNode previousVersion) { 27 | try { 28 | JsonNode newCompiler = JsonNodeFactories.array(new Function(){ 29 | @Override 30 | public JsonNode apply(JsonNode compiler) { 31 | return JsonNodeFactories.object( 32 | JsonNodeFactories.field("name", compiler.getNode("name")), 33 | JsonNodeFactories.field("path", compiler.getNode("path")), 34 | JsonNodeFactories.field("compiler_specific_args", JsonNodeFactories.object()) 35 | ); 36 | } 37 | }.map(previousVersion.getArrayNode("compilers"))); 38 | 39 | JsonRootNode newRoot = JsonNodeFactories.object( 40 | JsonNodeFactories.field("version", JsonNodeFactories.string(getVersion())), 41 | JsonNodeFactories.field("global_hotkey", previousVersion.getNode("global_hotkey")), 42 | JsonNodeFactories.field("compilers", newCompiler), 43 | JsonNodeFactories.field("task_groups", previousVersion.getNode("task_groups")) 44 | ); 45 | return newRoot; 46 | } catch (Exception e) { 47 | LOGGER.log(Level.WARNING, "Unable to convert json from previous version " + getPreviousVersion(), e); 48 | return null; 49 | } 50 | } 51 | 52 | @Override 53 | protected boolean internalImportData(Config config, JsonRootNode data) { 54 | LOGGER.warning("Unsupported import data at version " + getVersion()); 55 | return false; 56 | } 57 | } -------------------------------------------------------------------------------- /src/core/config/Parser1_8.java: -------------------------------------------------------------------------------- 1 | package core.config; 2 | 3 | import java.util.logging.Level; 4 | import java.util.logging.Logger; 5 | 6 | import utilities.Function; 7 | import utilities.JSONUtility; 8 | import argo.jdom.JsonNode; 9 | import argo.jdom.JsonNodeFactories; 10 | import argo.jdom.JsonRootNode; 11 | import core.languageHandler.Language; 12 | 13 | public class Parser1_8 extends ConfigParser { 14 | 15 | private static final Logger LOGGER = Logger.getLogger(Parser1_8.class.getName()); 16 | 17 | @Override 18 | protected String getVersion() { 19 | return "1.8"; 20 | } 21 | 22 | @Override 23 | protected String getPreviousVersion() { 24 | return "1.7"; 25 | } 26 | 27 | @Override 28 | protected JsonRootNode internalConvertFromPreviousVersion(JsonRootNode previousVersion) { 29 | try { 30 | JsonNode globalHotkeys = previousVersion.getNode("global_hotkey"); 31 | JsonNode result = JSONUtility.removeChild(previousVersion, "global_hotkey"); 32 | 33 | JsonNode globalSettings = JSONUtility.addChild(result.getNode("global_settings"), "global_hotkey", globalHotkeys); 34 | result = JSONUtility.replaceChild(result, "global_settings", globalSettings); 35 | result = JSONUtility.addChild(result, "ipc_settings", JsonNodeFactories.array( 36 | new Function(){ 37 | @Override 38 | public JsonNode apply(Language l) { 39 | return JsonNodeFactories.object( 40 | JsonNodeFactories.field("name", JsonNodeFactories.string(l.toString())), 41 | JsonNodeFactories.field("launch_at_startup", JsonNodeFactories.booleanNode(true)) 42 | ); 43 | } 44 | }.map(Language.values()) 45 | )); 46 | 47 | return result.getRootNode(); 48 | } catch (Exception e) { 49 | LOGGER.log(Level.WARNING, "Unable to convert json from previous version " + getPreviousVersion(), e); 50 | return null; 51 | } 52 | } 53 | 54 | @Override 55 | protected boolean internalImportData(Config config, JsonRootNode root) { 56 | ConfigParser parser = Config.getNextConfigParser(getVersion()); 57 | return parser.internalImportData(config, root); 58 | } 59 | } -------------------------------------------------------------------------------- /src/core/config/Parser1_4.java: -------------------------------------------------------------------------------- 1 | package core.config; 2 | 3 | import java.util.logging.Level; 4 | import java.util.logging.Logger; 5 | 6 | import utilities.Function; 7 | import utilities.JSONUtility; 8 | import argo.jdom.JsonNode; 9 | import argo.jdom.JsonNodeFactories; 10 | import argo.jdom.JsonRootNode; 11 | 12 | public class Parser1_4 extends ConfigParser { 13 | 14 | private static final Logger LOGGER = Logger.getLogger(Parser1_4.class.getName()); 15 | 16 | @Override 17 | protected String getVersion() { 18 | return "1.4"; 19 | } 20 | 21 | @Override 22 | protected String getPreviousVersion() { 23 | return "1.3"; 24 | } 25 | 26 | @Override 27 | protected JsonRootNode internalConvertFromPreviousVersion(JsonRootNode previousVersion) { 28 | try { 29 | JsonNode taskGroup = JsonNodeFactories.array(new Function(){ 30 | @Override 31 | public JsonNode apply(JsonNode taskGroup) { 32 | return JSONUtility.replaceChild(taskGroup, "tasks", JsonNodeFactories.array( 33 | new Function() { 34 | @Override 35 | public JsonNode apply(JsonNode task) { 36 | return JSONUtility.replaceChild(task, "hotkey", 37 | JsonNodeFactories.array(task.getNode("hotkey"))); 38 | } 39 | }.map(taskGroup.getArrayNode("tasks")))); 40 | } 41 | }.map(previousVersion.getArrayNode("task_groups"))); 42 | 43 | JsonRootNode newRoot = JsonNodeFactories.object( 44 | JsonNodeFactories.field("version", JsonNodeFactories.string(getVersion())), 45 | JsonNodeFactories.field("global_hotkey", previousVersion.getNode("global_hotkey")), 46 | JsonNodeFactories.field("compilers", previousVersion.getNode("compilers")), 47 | JsonNodeFactories.field("task_groups", taskGroup) 48 | ); 49 | return newRoot; 50 | } catch (Exception e) { 51 | LOGGER.log(Level.WARNING, "Unable to convert json from previous version " + getPreviousVersion(), e); 52 | return null; 53 | } 54 | } 55 | 56 | @Override 57 | protected boolean internalImportData(Config config, JsonRootNode data) { 58 | LOGGER.warning("Unsupported import data at version " + getVersion()); 59 | return false; 60 | } 61 | } -------------------------------------------------------------------------------- /src/core/config/Parser1_7.java: -------------------------------------------------------------------------------- 1 | package core.config; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.logging.Level; 6 | import java.util.logging.Logger; 7 | 8 | import utilities.JSONUtility; 9 | import argo.jdom.JsonNode; 10 | import argo.jdom.JsonNodeFactories; 11 | import argo.jdom.JsonRootNode; 12 | import core.userDefinedTask.UsageStatistics; 13 | 14 | public class Parser1_7 extends ConfigParser { 15 | 16 | private static final Logger LOGGER = Logger.getLogger(Parser1_7.class.getName()); 17 | 18 | @Override 19 | protected String getVersion() { 20 | return "1.7"; 21 | } 22 | 23 | @Override 24 | protected String getPreviousVersion() { 25 | return "1.6"; 26 | } 27 | 28 | private JsonNode internalTaskConversion(JsonNode tasks) { 29 | UsageStatistics reference = new UsageStatistics(); 30 | 31 | List converted = new ArrayList<>(); 32 | for (JsonNode child : tasks.getArrayNode()) { 33 | converted.add(JSONUtility.addChild(child, "statistics", reference.jsonize())); 34 | } 35 | return JsonNodeFactories.array(converted); 36 | } 37 | 38 | private JsonNode internalTaskGroupConversion(JsonNode node) { 39 | List converted = new ArrayList<>(); 40 | for (JsonNode child : node.getArrayNode()) { 41 | JsonNode tasksNode = child.getNode("tasks"); 42 | converted.add(JSONUtility.replaceChild(child, "tasks", internalTaskConversion(tasksNode))); 43 | } 44 | return JsonNodeFactories.array(converted); 45 | } 46 | 47 | @Override 48 | protected JsonRootNode internalConvertFromPreviousVersion(JsonRootNode previousVersion) { 49 | try { 50 | JsonNode replacing = previousVersion.getNode("task_groups"); 51 | JsonNode output = JSONUtility.replaceChild(previousVersion, "task_groups", internalTaskGroupConversion(replacing)); 52 | return output.getRootNode(); 53 | } catch (Exception e) { 54 | LOGGER.log(Level.WARNING, "Unable to convert json from previous version " + getPreviousVersion(), e); 55 | return null; 56 | } 57 | } 58 | 59 | @Override 60 | protected boolean internalImportData(Config config, JsonRootNode root) { 61 | Parser1_8 parser = new Parser1_8(); 62 | return parser.internalImportData(config, root); 63 | } 64 | } -------------------------------------------------------------------------------- /src/utilities/NumberUtility.java: -------------------------------------------------------------------------------- 1 | package utilities; 2 | 3 | 4 | public final class NumberUtility { 5 | 6 | private NumberUtility() {} 7 | 8 | public static boolean equalDouble(double a, double b) { 9 | return Math.abs(a - b) < 0.00001d; 10 | } 11 | 12 | public static boolean equalFloat(float a, float b) { 13 | return Math.abs(a - b) < 0.00001f; 14 | } 15 | 16 | public static int getDigit(int number, int digit) {//0 means lsb 17 | String num = number + ""; 18 | int lsb = num.length() - 1; 19 | lsb -= digit; 20 | if (lsb >= 0) { 21 | return num.charAt(lsb) - '0'; 22 | } else { 23 | return -1; 24 | } 25 | } 26 | 27 | public static boolean isInteger(double input) { 28 | return (input == Math.floor(input)) && !Double.isInfinite(input); 29 | } 30 | 31 | public static boolean isPositiveInteger(String input) { 32 | return isInteger(input) && Integer.parseInt(input) > 0; 33 | } 34 | 35 | public static boolean isNonNegativeInteger(String input) { 36 | return isInteger(input) && Integer.parseInt(input) >= 0; 37 | } 38 | 39 | public static boolean isInteger(String input) { 40 | if (input == null) { 41 | return false; 42 | } 43 | 44 | if (input.length() == 0) { 45 | return false; 46 | } 47 | 48 | input = input.replaceAll(",", ""); 49 | 50 | if (input.startsWith("-")) { 51 | input = input.substring(1); 52 | } 53 | 54 | for (int i = 0; i < input.length(); i++) { 55 | char c = input.charAt(i); 56 | if (c > '9' || c < '0') { 57 | return false; 58 | } 59 | } 60 | 61 | return true; 62 | } 63 | 64 | public static boolean isDouble(String input) { 65 | if (StringUtilities.countOccurrence(input, '.') > 1) { 66 | return false; 67 | } 68 | input = input.replaceAll("\\.", ""); 69 | 70 | return isInteger(input); 71 | } 72 | 73 | public static boolean inRange(int a, int lower, int upper) { 74 | return (a >= lower) && (a <= upper); 75 | } 76 | 77 | public static boolean inRange(double a, double lower, double upper) { 78 | return (a >= lower) && (a <= upper); 79 | } 80 | 81 | public static float fromIEEE754Binary(String binary) { 82 | int integer = (int) Long.parseLong(binary, 2); 83 | return Float.intBitsToFloat(integer); 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/core/keyChain/mouseGestureRecognition/DataNormalizer.java: -------------------------------------------------------------------------------- 1 | package core.keyChain.mouseGestureRecognition; 2 | 3 | import java.awt.Point; 4 | import java.util.ArrayList; 5 | 6 | public class DataNormalizer { 7 | 8 | public static final int POINT_COUNT = 35; 9 | public static final int FEATURE_COUNT = POINT_COUNT * 2; 10 | 11 | /** 12 | * Trim down the list of points to have exactly {@link POINT_COUNT}. 13 | * Then scale the points so that they fit into a unit square. 14 | * 15 | * @param input input list of points to normalize. 16 | * @return list of normalized points flattenned into (x1,y1,x2,y2,...). 17 | */ 18 | public ArrayList normalize(ArrayList input) { 19 | input = new DataDefinitionTrimmer().trim(input); 20 | 21 | int minX = Integer.MAX_VALUE, minY = Integer.MAX_VALUE; 22 | int maxX = Integer.MIN_VALUE, maxY = Integer.MIN_VALUE; 23 | 24 | for (Point p : input) { 25 | minX = Math.min(minX, p.x); 26 | minY = Math.min(minY, p.y); 27 | 28 | maxX = Math.max(maxX, p.x); 29 | maxY = Math.max(maxY, p.y); 30 | } 31 | 32 | int width = maxX - minX; 33 | width = width == 0 ? 1 : width; 34 | 35 | int height = maxY - minY; 36 | height = height == 0 ? 1 : height; 37 | 38 | float midX, midY, subX, subY; 39 | 40 | // Fit this into a square box. Center the appropriate dimension. 41 | if (height > width) { 42 | midX = (maxX + minX) / 2; 43 | subX = midX - height / 2; 44 | subY = minY; 45 | } else if (width > height) { 46 | subX = minX; 47 | midY = (maxY + minY) / 2; 48 | subY = midY - width / 2; 49 | } else { 50 | subX = minX; 51 | subY = minY; 52 | } 53 | 54 | ArrayList centered = new ArrayList<>(input.size()); 55 | for (int i = 0; i < input.size(); i++) { 56 | Point current = input.get(i); 57 | centered.add(new Point.Float(current.x - subX, current.y - subY)); 58 | } 59 | 60 | ArrayList output = new ArrayList<>(input.size() * 2); 61 | // Now make it into a unit square and flatten 62 | float scale = Math.max(width, height); 63 | for (int i = 0; i < centered.size(); i++) { 64 | Point.Float current = centered.get(i); 65 | output.add(current.x / scale); 66 | output.add(current.y / scale); 67 | } 68 | 69 | return output; 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/core/languageHandler/sourceGenerator/AbstractSourceGenerator.java: -------------------------------------------------------------------------------- 1 | package core.languageHandler.sourceGenerator; 2 | 3 | import java.util.Arrays; 4 | import java.util.HashMap; 5 | import java.util.Map; 6 | 7 | import core.languageHandler.Language; 8 | 9 | public abstract class AbstractSourceGenerator { 10 | 11 | protected final StringBuffer source; 12 | protected TaskSourceScheduler sourceScheduler; 13 | 14 | protected static final String TAB = " "; 15 | protected static final String TWO_TAB = TAB + TAB; 16 | protected static final String THREE_TAB = TWO_TAB + TAB; 17 | protected static final String FOUR_TAB = THREE_TAB + TAB; 18 | 19 | private static final Map REFERENCE_SOURCES; 20 | static { 21 | REFERENCE_SOURCES = new HashMap<>(); 22 | REFERENCE_SOURCES.put(Language.JAVA, new JavaSourceGenerator()); 23 | REFERENCE_SOURCES.put(Language.PYTHON, new PythonSourceGenerator()); 24 | REFERENCE_SOURCES.put(Language.CSHARP, new CSharpSourceGenerator()); 25 | } 26 | 27 | public static String getReferenceSource(Language language) { 28 | AbstractSourceGenerator generator = REFERENCE_SOURCES.get(language); 29 | if (generator != null) { 30 | return generator.getSource(); 31 | } 32 | return null; 33 | } 34 | 35 | public AbstractSourceGenerator() { 36 | source = new StringBuffer(); 37 | sourceScheduler = new TaskSourceScheduler(); 38 | } 39 | 40 | public final boolean submitTask(long time, String device, String action, int[] param) { 41 | if (!verify(device, action, param)) { 42 | return false; 43 | } 44 | 45 | return internalSubmitTask(time, device, action, param); 46 | } 47 | 48 | protected abstract boolean internalSubmitTask(long time, String device, String action, int[] param); 49 | 50 | protected final boolean verify(String device, String action, int[] param) { 51 | if (device.equals("mouse")) { 52 | return Arrays.asList("move", "moveBy", "press", "release", "click").contains(action); 53 | } else if (device.equals("keyBoard")) { 54 | return Arrays.asList("type", "press", "release").contains(action); 55 | } else { 56 | return false; 57 | } 58 | } 59 | 60 | public final void clear() { 61 | source.setLength(0); 62 | } 63 | 64 | public abstract String getSource(); 65 | } 66 | -------------------------------------------------------------------------------- /src/core/keyChain/mouseGestureRecognition/MouseGestureClassifier.java: -------------------------------------------------------------------------------- 1 | package core.keyChain.mouseGestureRecognition; 2 | 3 | import java.awt.Point; 4 | import java.util.ArrayList; 5 | import java.util.Queue; 6 | import java.util.logging.Level; 7 | import java.util.logging.Logger; 8 | 9 | import core.keyChain.MouseGesture; 10 | 11 | /** 12 | * Mouse gesture classification class. This class classifies a list of points as a mouse gesture. 13 | * 14 | * @author HP Truong 15 | */ 16 | public class MouseGestureClassifier { 17 | 18 | private static final Logger LOGGER = Logger.getLogger(MouseGestureClassifier.class.getName()); 19 | 20 | LogisticRegressionModel logisticRegressionModel; 21 | 22 | public MouseGestureClassifier() { 23 | logisticRegressionModel = new LogisticRegressionModel(); 24 | logisticRegressionModel.load(); 25 | } 26 | 27 | /** 28 | * Classify the mouse gesture given an {@link Queue} of points. 29 | * At the end of the method, the number of points considered during classification 30 | * will be removed from the queue. 31 | * 32 | * @param points a {@link Queue} of points ordered chronologically. 33 | * @param size the number of points to be considered in the queue. 34 | * @return the mouse gesture classified by the model. 35 | */ 36 | public MouseGesture classifyGesture(Queue points, int size) { 37 | if (size < DataNormalizer.POINT_COUNT) { 38 | LOGGER.log(Level.FINE, "Not enough points for classification. " 39 | + "Required at least {0} points but provided {1} points.", 40 | new Object[] {DataNormalizer.POINT_COUNT, size}); 41 | return MouseGesture.RANDOM; 42 | } 43 | 44 | ArrayList input = new ArrayList<>(size); 45 | for (int i = 0; i < size; i++) { 46 | input.add(points.poll()); 47 | } 48 | 49 | ArrayList normalized = new DataNormalizer().normalize(input); 50 | 51 | double[] normalizedArray = new double[normalized.size()]; 52 | int index = 0; 53 | for (Float f : normalized) { 54 | normalizedArray[index++] = f; 55 | } 56 | 57 | String prediction = logisticRegressionModel.predict(normalizedArray); 58 | if (prediction == null) { 59 | return MouseGesture.RANDOM; 60 | } 61 | 62 | MouseGesture result = MouseGesture.find(prediction); 63 | if (result == null) { 64 | return MouseGesture.RANDOM; 65 | } 66 | return result; 67 | } 68 | } 69 | -------------------------------------------------------------------------------- /src/core/ipc/repeatServer/processors/AbstractMessageProcessor.java: -------------------------------------------------------------------------------- 1 | package core.ipc.repeatServer.processors; 2 | 3 | import java.util.logging.Logger; 4 | 5 | import utilities.ILoggable; 6 | import argo.jdom.JsonNode; 7 | import argo.jdom.JsonNodeFactories; 8 | import core.ipc.repeatServer.MainMessageSender; 9 | 10 | abstract class AbstractMessageProcessor implements ILoggable { 11 | 12 | protected static final String SUCCESS_STATUS = "Success"; 13 | protected static final String FAILURE_STATUS = "Failure"; 14 | 15 | protected final MainMessageSender messageSender; 16 | 17 | protected AbstractMessageProcessor(MainMessageSender messageSender) { 18 | this.messageSender = messageSender; 19 | } 20 | 21 | public abstract boolean process(String type, long id, JsonNode content) throws InterruptedException; 22 | protected abstract boolean verifyMessageContent(JsonNode content); 23 | 24 | protected boolean verifyReplyContent(JsonNode content) { 25 | return content.isStringValue("status") && 26 | content.isNode("message"); 27 | } 28 | 29 | protected boolean success(String type, long id, String message) { 30 | return messageSender.sendMessage(type, id, generateReply(SUCCESS_STATUS, message)); 31 | } 32 | 33 | protected boolean success(String type, long id, JsonNode message) { 34 | return messageSender.sendMessage(type, id, generateReply(SUCCESS_STATUS, message)); 35 | } 36 | 37 | protected boolean success(String type, long id) { 38 | return success(type, id, ""); 39 | } 40 | 41 | protected boolean failure(String type, long id, String message) { 42 | getLogger().warning(message); 43 | messageSender.sendMessage(type, id, generateReply(FAILURE_STATUS, message)); 44 | return false; 45 | } 46 | 47 | protected JsonNode generateReply(String status, String message) { 48 | return JsonNodeFactories.object( 49 | JsonNodeFactories.field("status", JsonNodeFactories.string(status)), 50 | JsonNodeFactories.field("message", JsonNodeFactories.string(message)) 51 | ); 52 | } 53 | 54 | protected JsonNode generateReply(String status, JsonNode message) { 55 | return JsonNodeFactories.object( 56 | JsonNodeFactories.field("status", JsonNodeFactories.string(status)), 57 | JsonNodeFactories.field("message", message) 58 | ); 59 | } 60 | 61 | @Override 62 | public final Logger getLogger() { 63 | return Logger.getLogger(getClass().getName()); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /src/core/config/Parser1_3.java: -------------------------------------------------------------------------------- 1 | package core.config; 2 | 3 | import java.io.File; 4 | import java.util.logging.Level; 5 | import java.util.logging.Logger; 6 | 7 | import utilities.FileUtility; 8 | import utilities.Function; 9 | import utilities.JSONUtility; 10 | import argo.jdom.JsonNode; 11 | import argo.jdom.JsonNodeFactories; 12 | import argo.jdom.JsonRootNode; 13 | 14 | public class Parser1_3 extends ConfigParser { 15 | 16 | private static final Logger LOGGER = Logger.getLogger(Parser1_3.class.getName()); 17 | 18 | @Override 19 | protected String getVersion() { 20 | return "1.3"; 21 | } 22 | 23 | @Override 24 | protected String getPreviousVersion() { 25 | return "1.2"; 26 | } 27 | 28 | @Override 29 | protected JsonRootNode internalConvertFromPreviousVersion(JsonRootNode previousVersion) { 30 | try { 31 | final File currentPath = new File(""); 32 | JsonNode taskGroup = JsonNodeFactories.array(new Function(){ 33 | @Override 34 | public JsonNode apply(JsonNode taskGroup) { 35 | return JSONUtility.replaceChild(taskGroup, "tasks", JsonNodeFactories.array( 36 | new Function() { 37 | @Override 38 | public JsonNode apply(JsonNode task) { 39 | String sourcePath = task.getStringValue("source_path"); 40 | String relativePath = FileUtility.getRelativePath(currentPath, new File(sourcePath)); 41 | return JSONUtility.replaceChild(task, "source_path", JsonNodeFactories.string(relativePath)); 42 | } 43 | }.map(taskGroup.getArrayNode("tasks")))); 44 | } 45 | }.map(previousVersion.getArrayNode("task_groups"))); 46 | 47 | JsonRootNode newRoot = JsonNodeFactories.object( 48 | JsonNodeFactories.field("version", JsonNodeFactories.string(getVersion())), 49 | JsonNodeFactories.field("global_hotkey", previousVersion.getNode("global_hotkey")), 50 | JsonNodeFactories.field("compilers", previousVersion.getNode("compilers")), 51 | JsonNodeFactories.field("task_groups", taskGroup) 52 | ); 53 | return newRoot; 54 | } catch (Exception e) { 55 | LOGGER.log(Level.WARNING, "Unable to convert json from previous version " + getPreviousVersion(), e); 56 | return null; 57 | } 58 | } 59 | 60 | @Override 61 | protected boolean internalImportData(Config config, JsonRootNode data) { 62 | LOGGER.warning("Unsupported import data at version " + getVersion()); 63 | return false; 64 | } 65 | } -------------------------------------------------------------------------------- /src/core/config/Parser1_1.java: -------------------------------------------------------------------------------- 1 | package core.config; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.logging.Level; 6 | import java.util.logging.Logger; 7 | 8 | import argo.jdom.JsonNode; 9 | import argo.jdom.JsonNodeFactories; 10 | import argo.jdom.JsonRootNode; 11 | 12 | public class Parser1_1 extends ConfigParser { 13 | 14 | private static final Logger LOGGER = Logger.getLogger(Parser1_1.class.getName()); 15 | 16 | @Override 17 | protected String getVersion() { 18 | return "1.1"; 19 | } 20 | 21 | @Override 22 | protected String getPreviousVersion() { 23 | return "1.0"; 24 | } 25 | 26 | @Override 27 | protected JsonRootNode internalConvertFromPreviousVersion(JsonRootNode previousVersion) { 28 | try { 29 | List newTask = new ArrayList<>(); 30 | for (JsonNode oldTask : previousVersion.getArrayNode("tasks")) { 31 | newTask.add(JsonNodeFactories.object( 32 | JsonNodeFactories.field("source_path", oldTask.getNode("source_path")), 33 | JsonNodeFactories.field("compiler", oldTask.getNode("compiler")), 34 | JsonNodeFactories.field("name", oldTask.getNode("name")), 35 | JsonNodeFactories.field("hotkey", oldTask.getNode("hotkey")), 36 | JsonNodeFactories.field("enabled", JsonNodeFactories.booleanNode(true)) 37 | )); 38 | } 39 | JsonNode onlyGroup = JsonNodeFactories.array(JsonNodeFactories.object( 40 | JsonNodeFactories.field("name", JsonNodeFactories.string("default")), 41 | JsonNodeFactories.field("enabled", JsonNodeFactories.booleanNode(true)), 42 | JsonNodeFactories.field("tasks", JsonNodeFactories.array(newTask)) 43 | )); 44 | 45 | 46 | JsonRootNode newRoot = JsonNodeFactories.object( 47 | JsonNodeFactories.field("version", JsonNodeFactories.string(getVersion())), 48 | JsonNodeFactories.field("global_hotkey", previousVersion.getNode("global_hotkey")), 49 | JsonNodeFactories.field("compilers", previousVersion.getNode("compilers")), 50 | JsonNodeFactories.field("task_groups", onlyGroup) 51 | ); 52 | 53 | return newRoot; 54 | } catch (Exception e) { 55 | LOGGER.log(Level.WARNING, "Unable to convert json from previous version " + getPreviousVersion(), e); 56 | return null; 57 | } 58 | } 59 | 60 | @Override 61 | protected boolean internalImportData(Config config, JsonRootNode data) { 62 | LOGGER.warning("Unsupported import data at version " + getVersion()); 63 | return false; 64 | } 65 | } -------------------------------------------------------------------------------- /src/core/keyChain/mouseGestureRecognition/LogisticRegressionModel.java: -------------------------------------------------------------------------------- 1 | package core.keyChain.mouseGestureRecognition; 2 | 3 | 4 | import staticResources.MouseGestureModelResources; 5 | import Jama.Matrix; 6 | 7 | /** 8 | * Logistic regression model raw implementation. This only implements the predict function. 9 | * 10 | * @author HP Truong 11 | */ 12 | class LogisticRegressionModel { 13 | 14 | private String[] labels; // classifying classes 15 | private Matrix w; // coefficients 16 | private Matrix c; // intercepts 17 | 18 | /** 19 | * Load model data from static resources. 20 | * 21 | * @return if operation was successful. 22 | */ 23 | protected boolean load() { 24 | labels = MouseGestureModelResources.getLabels(); 25 | if (labels == null) { 26 | return false; 27 | } 28 | 29 | double[] intercepts = MouseGestureModelResources.getIntercepts(); 30 | double[][] coefficients = MouseGestureModelResources.getCoefficients(labels.length); 31 | 32 | w = new Matrix(coefficients); 33 | 34 | double[][] twoDimensionalIntercept = new double[1][intercepts.length]; 35 | twoDimensionalIntercept[0] = intercepts; 36 | c = new Matrix(twoDimensionalIntercept); 37 | 38 | return isLoaded(); 39 | } 40 | 41 | /** 42 | * Predict using logistic regression and the loaded values for the model. 43 | * Specifically, this performs the matrix operation: 44 | * prediction = x * {@link #w}.T + {@link #c} 45 | * where {@link w}.T is the transpose of {@link w} 46 | * and select the label with highest prediction value. 47 | * 48 | * @param x the values of the features. 49 | * @return predicted label, or null if problem occurs. 50 | */ 51 | protected String predict(double[] featureValues) { 52 | if (!isLoaded()) { 53 | return null; 54 | } 55 | 56 | double[][] values = new double[1][featureValues.length]; 57 | values[0] = featureValues; 58 | Matrix x = new Matrix(values); 59 | Matrix result = x.times(w.transpose()).plus(c); 60 | 61 | double[] probabilities = result.getArray()[0]; 62 | int maxIndex = -1; 63 | double maxValue = Double.NEGATIVE_INFINITY; 64 | for (int i = 0; i < probabilities.length; i++) { 65 | double p = probabilities[i]; 66 | if (p > maxValue) { 67 | maxValue = p; 68 | maxIndex = i; 69 | } 70 | } 71 | 72 | return labels[maxIndex]; 73 | } 74 | 75 | /** 76 | * @return whether model was loaded successfully. 77 | */ 78 | protected boolean isLoaded() { 79 | return labels != null && w != null && c != null; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /src/core/controller/Core.java: -------------------------------------------------------------------------------- 1 | package core.controller; 2 | 3 | import java.awt.AWTException; 4 | import java.awt.Robot; 5 | import java.util.concurrent.ScheduledThreadPoolExecutor; 6 | import java.util.concurrent.TimeUnit; 7 | import java.util.logging.Level; 8 | import java.util.logging.Logger; 9 | 10 | public final class Core { 11 | 12 | private static final Logger LOGGER = Logger.getLogger(Core.class.getName()); 13 | private static final Core SINGLETON_INSTANCE = new Core(); 14 | 15 | private final ScheduledThreadPoolExecutor executor; 16 | private Robot controller; 17 | 18 | private final MouseCore mouse; 19 | private final KeyboardCore keyboard; 20 | 21 | private Core() { 22 | try { 23 | controller = new Robot(); 24 | } catch (AWTException e) { 25 | LOGGER.log(Level.SEVERE, "Exception constructing controller", e); 26 | System.exit(1); 27 | } 28 | 29 | executor = new ScheduledThreadPoolExecutor(10); 30 | mouse = new MouseCore(controller); 31 | keyboard = new KeyboardCore(controller); 32 | } 33 | 34 | public static Core getInstance() { 35 | return SINGLETON_INSTANCE; 36 | } 37 | 38 | /** 39 | * Unsafe method since not interruptible. Use at own risk 40 | * @param duration duration for controller to wait 41 | * @param callBack action to perform after wait duration 42 | * @deprecated unsafe and difficult use of callback 43 | */ 44 | @Deprecated 45 | protected void wait(int duration, Runnable callBack) { 46 | executor.schedule(callBack, duration, TimeUnit.MILLISECONDS); 47 | } 48 | 49 | /** 50 | * Blocking wait the current action for an amount of time 51 | * @param duration wait duration in milliseconds 52 | * @throws InterruptedException 53 | */ 54 | public void blockingWait(int duration) throws InterruptedException { 55 | Thread.sleep(duration); 56 | } 57 | 58 | /** 59 | * A short alias for blockingWait 60 | * @param duration wait duration in milliseconds 61 | * @throws InterruptedException 62 | */ 63 | public void delay(int duration) throws InterruptedException { 64 | blockingWait(duration); 65 | } 66 | 67 | /** 68 | * Getter for the mouse attribute. See {@link core.controller.MouseCore} class 69 | * @return The mouse controller attribute 70 | */ 71 | public MouseCore mouse() { 72 | return mouse; 73 | } 74 | 75 | /** 76 | * Getter for the mouse attribute. See {@link core.controller.KeyboardCore} class 77 | * @return The keyboard controller attribute 78 | */ 79 | public KeyboardCore keyBoard() { 80 | return keyboard; 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /src/core/languageHandler/sourceGenerator/PythonSourceGenerator.java: -------------------------------------------------------------------------------- 1 | package core.languageHandler.sourceGenerator; 2 | 3 | import java.util.logging.Logger; 4 | 5 | import staticResources.BootStrapResources; 6 | import utilities.Function; 7 | import core.languageHandler.Language; 8 | import core.scheduler.SchedulingData; 9 | 10 | public class PythonSourceGenerator extends AbstractSourceGenerator { 11 | 12 | private static final Logger LOGGER = Logger.getLogger(PythonSourceGenerator.class.getName()); 13 | 14 | public PythonSourceGenerator() { 15 | super(); 16 | this.sourceScheduler.setSleepSource(new Function() { 17 | @Override 18 | public String apply(Long r) { 19 | return TAB + "time.sleep(" + (r / 1000.0) + ")\n"; 20 | } 21 | }); 22 | } 23 | 24 | @Override 25 | public boolean internalSubmitTask(long time, String device, String action, int[] param) { 26 | String mid = ""; 27 | if (device.equals("mouse")) { 28 | if (action.equals("move")) { 29 | mid = "repeat_lib.mouse_move(" + param[0] + ", " + param[1] +")\n"; 30 | } else if (action.equals("moveBy")) { 31 | mid = "repeat_lib.mouse_move_by(" + param[0] + ", " + param[1] +")\n"; 32 | } else if (action.equals("click")) { 33 | mid = "repeat_lib.mouse_click(" + param[0] + ")\n"; 34 | } else if (action.equals("press")) { 35 | mid = "repeat_lib.mouse_press(" + param[0] + ")\n"; 36 | } else if (action.equals("release")) { 37 | mid = "repeat_lib.mouse_release(" + param[0] + ")\n"; 38 | } else { 39 | return false; 40 | } 41 | } else if (device.equals("keyBoard")) { 42 | if (action.equals("type")) { 43 | mid = "repeat_lib.key_type(" + param[0] + ")\n"; 44 | } else if (action.equals("press")) { 45 | mid = "repeat_lib.key_press(" + param[0] + ")\n"; 46 | } else if (action.equals("release")) { 47 | mid = "repeat_lib.key_release(" + param[0] + ")\n"; 48 | } else { 49 | return false; 50 | } 51 | } else if (action.equals("wait")) { 52 | mid = "repeat_lib.blockingWait(" + param[0] + ")\n"; 53 | } 54 | 55 | return sourceScheduler.addTask(new SchedulingData(time, TAB + mid)); 56 | } 57 | 58 | @Override 59 | public String getSource() { 60 | String mainSource = sourceScheduler.getSource(); 61 | if (mainSource == null) { 62 | LOGGER.severe("Unable to generate source..."); 63 | mainSource = ""; 64 | } 65 | 66 | StringBuffer sb = new StringBuffer(); 67 | sb.append(BootStrapResources.getNativeLanguageTemplate(Language.PYTHON)); 68 | sb.append(mainSource); 69 | 70 | return sb.toString(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/core/languageHandler/sourceGenerator/CSharpSourceGenerator.java: -------------------------------------------------------------------------------- 1 | package core.languageHandler.sourceGenerator; 2 | 3 | import java.util.logging.Logger; 4 | 5 | import staticResources.BootStrapResources; 6 | import utilities.Function; 7 | import core.languageHandler.Language; 8 | import core.scheduler.SchedulingData; 9 | 10 | public class CSharpSourceGenerator extends AbstractSourceGenerator { 11 | 12 | private static final Logger LOGGER = Logger.getLogger(CSharpSourceGenerator.class.getName()); 13 | 14 | public CSharpSourceGenerator() { 15 | super(); 16 | this.sourceScheduler.setSleepSource(new Function() { 17 | @Override 18 | public String apply(Long r) { 19 | return ""; 20 | } 21 | }); 22 | } 23 | 24 | @Override 25 | public boolean internalSubmitTask(long time, String device, String action, int[] param) { 26 | String mid = ""; 27 | if (device.equals("mouse")) { 28 | if (action.equals("move")) { 29 | mid = "controller.mouse().move(" + param[0] + ", " + param[1] +");\n"; 30 | } else if (action.equals("moveBy")) { 31 | mid = "controller.mouse().moveBy(" + param[0] + ", " + param[1] +");\n"; 32 | } else if (action.equals("click")) { 33 | mid = "controller.mouse().click(" + param[0] + ");\n"; 34 | } else if (action.equals("press")) { 35 | mid = "controller.mouse().press(" + param[0] + ");\n"; 36 | } else if (action.equals("release")) { 37 | mid = "controller.mouse().release(" + param[0] + ");\n"; 38 | } else { 39 | return false; 40 | } 41 | } else if (device.equals("keyBoard")) { 42 | if (action.equals("type")) { 43 | mid = "controller.keyBoard().type(" + param[0] + ");\n"; 44 | } else if (action.equals("press")) { 45 | mid = "controller.keyBoard().press(" + param[0] + ");\n"; 46 | } else if (action.equals("release")) { 47 | mid = "controller.keyBoard().release(" + param[0] + ");\n"; 48 | } else { 49 | return false; 50 | } 51 | } else if (action.equals("wait")) { 52 | mid = "controller.blockingWait(" + param[0] + ");\n"; 53 | } 54 | 55 | return sourceScheduler.addTask(new SchedulingData(time, FOUR_TAB + mid)); 56 | } 57 | 58 | @Override 59 | public String getSource() { 60 | String mainSource = sourceScheduler.getSource(); 61 | if (mainSource == null) { 62 | LOGGER.severe("Unable to generate source..."); 63 | mainSource = ""; 64 | } 65 | 66 | StringBuffer sb = new StringBuffer(); 67 | sb.append(BootStrapResources.getNativeLanguageTemplate(Language.CSHARP)); 68 | sb.append(mainSource); 69 | 70 | return sb.toString(); 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/core/userDefinedTask/Tools.java: -------------------------------------------------------------------------------- 1 | package core.userDefinedTask; 2 | 3 | import java.awt.HeadlessException; 4 | import java.awt.Toolkit; 5 | import java.awt.datatransfer.Clipboard; 6 | import java.awt.datatransfer.DataFlavor; 7 | import java.awt.datatransfer.StringSelection; 8 | import java.awt.datatransfer.UnsupportedFlavorException; 9 | import java.io.File; 10 | import java.io.IOException; 11 | import java.util.logging.Level; 12 | import java.util.logging.Logger; 13 | 14 | import utilities.SubprocessUttility; 15 | 16 | public class Tools { 17 | 18 | private static final Logger LOGGER = Logger.getLogger(Tools.class.getName()); 19 | 20 | /** 21 | * Get plain text (if possible) from system clipboard 22 | * @return the plain text in the clipboard, or empty string if encounter an error 23 | */ 24 | public static String getClipboard() { 25 | try { 26 | String data = (String) Toolkit.getDefaultToolkit().getSystemClipboard().getData(DataFlavor.stringFlavor); 27 | return data; 28 | } catch (HeadlessException | UnsupportedFlavorException | IOException e) { 29 | LOGGER.log(Level.WARNING, "Unable to retrieve text from clipboard", e); 30 | return ""; 31 | } 32 | } 33 | 34 | /** 35 | * Set a text value into the system clipboard 36 | * @param data string to copy to the system clipboard 37 | * @return if action succeeds 38 | */ 39 | public static boolean setClipboard(String data) { 40 | Clipboard clipboard = Toolkit.getDefaultToolkit().getSystemClipboard(); 41 | StringSelection selection = new StringSelection(data); 42 | clipboard.setContents(selection, null); 43 | 44 | return true; 45 | } 46 | 47 | /** 48 | * Execute a command in the environment 49 | * @param command command to execute 50 | * @return stdout of the command 51 | */ 52 | public static String execute(String command) { 53 | return SubprocessUttility.execute(command); 54 | } 55 | 56 | /** 57 | * Execute a command in a specific directory 58 | * @param command command to execute 59 | * @param cwd directory where the command should be executed in 60 | * @return stdout of the command 61 | */ 62 | public static String execute(String command, String cwd) { 63 | return execute(command, new File(cwd)); 64 | } 65 | 66 | /** 67 | * Execute a command in a specific directory 68 | * @param command command to execute 69 | * @param cwd directory where the command should be executed in 70 | * @return stdout of the command 71 | */ 72 | public static String execute(String command, File cwd) { 73 | return SubprocessUttility.execute(command, cwd); 74 | } 75 | 76 | private Tools() {} 77 | } 78 | -------------------------------------------------------------------------------- /src/core/languageHandler/sourceGenerator/JavaSourceGenerator.java: -------------------------------------------------------------------------------- 1 | package core.languageHandler.sourceGenerator; 2 | 3 | import java.util.logging.Logger; 4 | 5 | import staticResources.BootStrapResources; 6 | import utilities.Function; 7 | import core.languageHandler.Language; 8 | import core.scheduler.SchedulingData; 9 | 10 | public class JavaSourceGenerator extends AbstractSourceGenerator { 11 | 12 | private static final Logger LOGGER = Logger.getLogger(JavaSourceGenerator.class.getName()); 13 | 14 | public JavaSourceGenerator() { 15 | super(); 16 | this.sourceScheduler.setSleepSource(new Function() { 17 | @Override 18 | public String apply(Long r) { 19 | return FOUR_TAB + "controller.blockingWait(" + r + ");\n"; 20 | } 21 | }); 22 | } 23 | 24 | @Override 25 | public boolean internalSubmitTask(long time, String device, String action, int[] param) { 26 | String mid = ""; 27 | if (device.equals("mouse")) { 28 | if (action.equals("move")) { 29 | mid = "controller.mouse().move(" + param[0] + ", " + param[1] +");\n"; 30 | } else if (action.equals("moveBy")) { 31 | mid = "controller.mouse().moveBy(" + param[0] + ", " + param[1] +");\n"; 32 | } else if (action.equals("click")) { 33 | mid = "controller.mouse().click(" + param[0] + ");\n"; 34 | } else if (action.equals("press")) { 35 | mid = "controller.mouse().press(" + param[0] + ");\n"; 36 | } else if (action.equals("release")) { 37 | mid = "controller.mouse().release(" + param[0] + ");\n"; 38 | } else { 39 | return false; 40 | } 41 | } else if (device.equals("keyBoard")) { 42 | if (action.equals("type")) { 43 | mid = "controller.keyBoard().type(" + param[0] + ");\n"; 44 | } else if (action.equals("press")) { 45 | mid = "controller.keyBoard().press(" + param[0] + ");\n"; 46 | } else if (action.equals("release")) { 47 | mid = "controller.keyBoard().release(" + param[0] + ");\n"; 48 | } else { 49 | return false; 50 | } 51 | } else if (action.equals("wait")) { 52 | mid = "controller.blockingWait(" + param[0] + ");\n"; 53 | } 54 | 55 | return sourceScheduler.addTask(new SchedulingData(time, FOUR_TAB + mid)); 56 | } 57 | 58 | @Override 59 | public String getSource() { 60 | String mainSource = sourceScheduler.getSource(); 61 | if (mainSource == null) { 62 | LOGGER.severe("Unable to generate source..."); 63 | mainSource = ""; 64 | } 65 | 66 | StringBuffer sb = new StringBuffer(); 67 | sb.append(BootStrapResources.getNativeLanguageTemplate(Language.JAVA)); 68 | sb.append(mainSource); 69 | return sb.toString(); 70 | } 71 | } 72 | -------------------------------------------------------------------------------- /src/core/ipc/repeatServer/MainMessageSender.java: -------------------------------------------------------------------------------- 1 | package core.ipc.repeatServer; 2 | 3 | import java.io.BufferedWriter; 4 | import java.io.IOException; 5 | import java.util.logging.Level; 6 | import java.util.logging.Logger; 7 | 8 | import utilities.ILoggable; 9 | import utilities.JSONUtility; 10 | import argo.jdom.JsonNode; 11 | import argo.jdom.JsonNodeFactories; 12 | import argo.jdom.JsonRootNode; 13 | 14 | /** 15 | * This class sends messages to the ipc client 16 | * A generic message will have the following JSON format: 17 | * { 18 | * "id" : id of the message as integer, 19 | * "type" : type of the message as string. See {@link core.ipc.repeatServer.processors.ServerMainProcessor} 20 | * "content" : JSON content of the message (determined by upper layer) 21 | * } 22 | * 23 | * @author HP Truong 24 | * 25 | */ 26 | public class MainMessageSender implements ILoggable { 27 | 28 | private long idCount; 29 | private BufferedWriter writer; 30 | 31 | protected MainMessageSender() { 32 | idCount = 1L; 33 | } 34 | 35 | public long sendMessage(String type, JsonNode content) { 36 | long id = newID(); 37 | if (sendMessage(type, id, content)) { 38 | return id; 39 | } else { 40 | return -1; 41 | } 42 | } 43 | 44 | public boolean sendMessage(String type, long id, JsonNode content) { 45 | JsonRootNode toSend = getMessage(type, id, content); 46 | 47 | synchronized (this) { 48 | try { 49 | String message = String.format("%s%s%s%s%s", 50 | ClientServingThread.MESSAGE_DELIMITER, 51 | ClientServingThread.MESSAGE_DELIMITER, 52 | JSONUtility.jsonToString(toSend), 53 | ClientServingThread.MESSAGE_DELIMITER, 54 | ClientServingThread.MESSAGE_DELIMITER); 55 | writer.write(message); 56 | writer.flush(); 57 | } catch (IOException e) { 58 | getLogger().log(Level.WARNING, "Exception while writing message", e); 59 | return false; 60 | } 61 | } 62 | 63 | return true; 64 | } 65 | 66 | private JsonRootNode getMessage(String type, long id, JsonNode message) { 67 | return JsonNodeFactories.object( 68 | JsonNodeFactories.field("type", JsonNodeFactories.string(type)), 69 | JsonNodeFactories.field("id", JsonNodeFactories.number(id)), 70 | JsonNodeFactories.field("content", message) 71 | ); 72 | } 73 | 74 | private synchronized long newID() { 75 | idCount++; 76 | return idCount; 77 | } 78 | 79 | protected void setWriter(BufferedWriter writer) { 80 | this.writer = writer; 81 | } 82 | 83 | @Override 84 | public Logger getLogger() { 85 | return Logger.getLogger(MainMessageSender.class.getName()); 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /src/staticResources/MouseGestureModelResources.java: -------------------------------------------------------------------------------- 1 | package staticResources; 2 | 3 | import java.util.logging.Level; 4 | import java.util.logging.Logger; 5 | 6 | import utilities.NumberUtility; 7 | 8 | /** 9 | * Handle the loading and parsing of mouse gesture recognition model. 10 | */ 11 | public class MouseGestureModelResources { 12 | 13 | private static final Logger LOGGER = Logger.getLogger(MouseGestureModelResources.class.getName()); 14 | 15 | private static final String INTERCEPTS_FILE = "/mouseGestureModel/intercepts"; 16 | private static final String COEFFICIENTS_FILE = "/mouseGestureModel/coefficients"; 17 | private static final String LABELS_FILE = "/mouseGestureModel/labels"; 18 | 19 | /** 20 | * Load the intercepts from file. Each line is an intercept in IEEE 754 single precision form. 21 | * 22 | * @return array of intercepts. 23 | */ 24 | public static double[] getIntercepts() { 25 | String content = BootStrapResources.getFile(INTERCEPTS_FILE); 26 | String[] lines = content.split("\n"); 27 | 28 | double[] output = new double[lines.length]; 29 | for (int i = 0; i < lines.length; i++) { 30 | try { 31 | float value = NumberUtility.fromIEEE754Binary(lines[i]); 32 | output[i] = value; 33 | } catch (NumberFormatException e) { 34 | LOGGER.log(Level.WARNING, e.getMessage(), e); 35 | return null; 36 | } 37 | } 38 | 39 | return output; 40 | } 41 | 42 | /** 43 | * Load the coefficients matrix from file. Each line is an intercept in IEEE 754 single precision form. 44 | * Note that since the feature space and classes space are small, it is OK to store matrix in 2D array 45 | * without worrying about performance issue. 46 | * 47 | * 48 | * @param labelCount number of label, which is the same as number of row for this matrix. 49 | * @return the coefficients matrix. 50 | */ 51 | public static double[][] getCoefficients(int labelCount) { 52 | String content = BootStrapResources.getFile(COEFFICIENTS_FILE); 53 | String[] lines = content.split("\n"); 54 | 55 | int colCount = lines.length / labelCount; 56 | double[][] output = new double[labelCount][colCount]; 57 | 58 | int row = 0, col = 0; 59 | for (String line : lines) { 60 | try { 61 | float value = NumberUtility.fromIEEE754Binary(line); 62 | output[row][col++] = value; 63 | } catch (NumberFormatException e) { 64 | LOGGER.log(Level.WARNING, e.getMessage(), e); 65 | return null; 66 | } 67 | 68 | if (col == colCount) { 69 | col = 0; 70 | row++; 71 | } 72 | } 73 | 74 | return output; 75 | } 76 | 77 | /** 78 | * Load the list of labels from file. Each line is a label name. 79 | * 80 | * @return list of labels loaded. 81 | */ 82 | public static String[] getLabels() { 83 | String content = BootStrapResources.getFile(LABELS_FILE); 84 | if (content == null) { 85 | return null; 86 | } 87 | 88 | return content.split("\n"); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/core/languageHandler/API/JavaAPI.txt: -------------------------------------------------------------------------------- 1 | Repeat - Java API 2 | Updated 16th April 2015 3 | Author: HP Truong 4 | 5 | ********************************************************************************************************** 6 | 7 | I. Class method and attributes: 8 | Use these anywhere in the method you define. 9 | 1) executeInGroup(int index): execute task with index number from the same task group. Invalid index 10 | does nothing. This method is deprecated and is no longer supported. 11 | 2) invokingKeyChain: store information about the key chain that invoked the task 12 | 13 | II. Core API: (variable controller of type Core) 14 | Use these anywhere with the controller variable: controller. 15 | 1) mouse(): get MouseCore 16 | 2) keyBoard(): get KeyboardCore 17 | 3) blockingWait(int milliseconds): wait for milliseconds before executing next command 18 | 4) delay(int milliseconds): an alias for blockingWait method 19 | 20 | III. MouseCore API: (variable mouse of type MouseCore) 21 | Use these anywhere with mouse variable: mouse. 22 | 1) getPosition() 23 | 2) getColor() 24 | 3) getColor(int x, int y) 25 | 4) click(int mask) 26 | 5) click(int mask, int delay) 27 | 6) leftClick() 28 | 7) move(int newX, int newY) 29 | 8) drag(int sourceX, int sourceY, int destX, int destY) 30 | 9) moveBy(int amountX, int amountY) 31 | 10) dragBy(int amountX, int amountY) 32 | 11) leftClicK(int delay); 33 | 12) rightClick(); 34 | 13) rightClick(int delay); 35 | 14) hold(int mask, int duration) 36 | 15) click(int mask, int x, int y) 37 | 16) click(int mask, Point p) 38 | 17) leftClick(int x, int y) 39 | 18) leftClick(int x, int y, int delay) 40 | 19) rightClick(int x, int y) 41 | 20) rightClick(int x, int y, int delay) 42 | 21) press(int mask) 43 | 22) release(int mask) 44 | 23) move(int newX, int newY) 45 | 24) moveBy(int amountX, int amountY) 46 | 47 | 48 | IV. KeyboardCore API: (variable key of type KeyboardCore) 49 | Use these anywhere with key variable: key. 50 | 1) type(String string) 51 | 2) type(char c) 52 | 3) type(int key) 53 | 4) type(int...keys) 54 | 5) typeRepeat(int key, int count); 55 | 6) combination(int...keys) 56 | 7) hold(int key, int duration) 57 | 8) press(int key) 58 | 9) press(int...keys) 59 | 10) release(int key) 60 | 11) release(int...keys) 61 | 12) isLocked(int key) 62 | 63 | V. KeyChain API: (variable invoker of type KeyChain) 64 | Use these anywhere with key variable: invoker. 65 | 1) getKeys(): return a list of integers representing the key chain that invoked this. 66 | 67 | VI. Shared memory API: (sharing variables between tasks) 68 | these anywhere with key variable: shared_memory. 69 | 1) getVar(String) 70 | 2) setVar(String, String) 71 | 3) delVar(String) -------------------------------------------------------------------------------- /src/core/languageHandler/compiler/DynamicCompilerManager.java: -------------------------------------------------------------------------------- 1 | package core.languageHandler.compiler; 2 | 3 | import java.io.File; 4 | import java.util.ArrayList; 5 | import java.util.HashMap; 6 | import java.util.List; 7 | import java.util.Map; 8 | import java.util.logging.Level; 9 | import java.util.logging.Logger; 10 | 11 | import utilities.FileUtility; 12 | import utilities.IJsonable; 13 | import argo.jdom.JsonNode; 14 | import argo.jdom.JsonNodeFactories; 15 | import argo.jdom.JsonRootNode; 16 | import core.languageHandler.Language; 17 | 18 | public class DynamicCompilerManager implements IJsonable { 19 | 20 | private static final Logger LOGGER = Logger.getLogger(DynamicCompilerManager.class.getName()); 21 | private final Map compilers; 22 | 23 | public DynamicCompilerManager() { 24 | compilers = new HashMap<>(); 25 | compilers.put(Language.JAVA, new JavaNativeCompiler("CustomAction", new String[]{"core"}, new String[]{})); 26 | compilers.put(Language.PYTHON, new PythonRemoteCompiler(new File("core"))); 27 | compilers.put(Language.CSHARP, new CSharpRemoteCompiler(new File("core"))); 28 | } 29 | 30 | public AbstractNativeCompiler getCompiler(Language name) { 31 | return compilers.get(name); 32 | } 33 | 34 | public AbstractNativeCompiler getCompiler(String name) { 35 | return getCompiler(Language.identify(name)); 36 | } 37 | 38 | public boolean hasCompiler(String name) { 39 | return compilers.containsKey(name); 40 | } 41 | 42 | public AbstractNativeCompiler removeCompiler(String name) { 43 | return compilers.remove(name); 44 | } 45 | 46 | @Override 47 | public JsonRootNode jsonize() { 48 | List compilerList = new ArrayList<>(); 49 | for (AbstractNativeCompiler compiler : compilers.values()) { 50 | compilerList.add(JsonNodeFactories.object( 51 | JsonNodeFactories.field("name", JsonNodeFactories.string(compiler.getName().toString())), 52 | JsonNodeFactories.field("path", JsonNodeFactories.string(FileUtility.getRelativePwdPath(compiler.getPath()))), 53 | JsonNodeFactories.field("compiler_specific_args", compiler.getCompilerSpecificArgs()) 54 | )); 55 | } 56 | 57 | return JsonNodeFactories.array(compilerList); 58 | } 59 | 60 | public boolean parseJSON(List compilerSettings) { 61 | for (JsonNode compilerNode : compilerSettings) { 62 | String name = compilerNode.getStringValue("name"); 63 | String path = compilerNode.getStringValue("path"); 64 | JsonNode compilerSpecificArgs = compilerNode.getNode("compiler_specific_args"); 65 | 66 | AbstractNativeCompiler compiler = getCompiler(name); 67 | if (compiler != null) { 68 | compiler.setPath(new File(path)); 69 | if (!compiler.parseCompilerSpecificArgs(compilerSpecificArgs)) { 70 | LOGGER.log(Level.WARNING, "Compiler " + name + " was unable to parse its specific arguments."); 71 | } 72 | } else { 73 | throw new IllegalStateException("Unknown compiler " + name); 74 | } 75 | } 76 | return true; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/core/keyChain/MouseGesture.java: -------------------------------------------------------------------------------- 1 | package core.keyChain; 2 | 3 | import java.util.ArrayList; 4 | import java.util.Arrays; 5 | import java.util.HashSet; 6 | import java.util.List; 7 | import java.util.Set; 8 | 9 | import utilities.IJsonable; 10 | import argo.jdom.JsonNode; 11 | import argo.jdom.JsonNodeFactories; 12 | import argo.jdom.JsonRootNode; 13 | 14 | /** 15 | * Enum representing classification categories 16 | * 17 | */ 18 | public enum MouseGesture implements IJsonable { 19 | ALPHA("alpha"), 20 | CIRCLE_LEFT("circle_left"), 21 | GAMMA("gamma"), 22 | GREATER_THAN("greater_than"), 23 | HAT("hat"), 24 | HORIZONTAL("horizontal"), 25 | LESS_THAN("less_than"), 26 | N("N"), 27 | RANDOM("random"), 28 | SIX("six"), 29 | SQUARE("square"), 30 | SQUARE_ROOT("square_root"), 31 | TILDA("tilda"), 32 | TRIANGLE("triangle"), 33 | U("u"), 34 | VERTICAL("vertical"), 35 | Z("z") 36 | ; 37 | 38 | public static final Set IGNORED_CLASSIFICATIONS = new HashSet<>( 39 | Arrays.asList(HORIZONTAL, 40 | VERTICAL, 41 | SIX, 42 | U, 43 | RANDOM)); 44 | 45 | private final String text; 46 | 47 | /** 48 | * @param text human readable text form of this classification 49 | */ 50 | private MouseGesture(final String text) { 51 | this.text = text; 52 | } 53 | 54 | /** 55 | * @return list of enabled mouse gestures that can be used to activate tasks. 56 | */ 57 | public static List enabledGestures() { 58 | List output = new ArrayList<>(); 59 | 60 | for (MouseGesture value : values()) { 61 | if (!IGNORED_CLASSIFICATIONS.contains(value)) { 62 | output.add(value); 63 | } 64 | } 65 | 66 | return output; 67 | } 68 | 69 | /** 70 | * Find the mouse gesture given its name. 71 | * 72 | * @param name name of the mouse gesture 73 | * @return the found mouse gesture, or null if cannot find one 74 | */ 75 | public static MouseGesture find(String name) { 76 | for (MouseGesture classification : MouseGesture.values()) { 77 | if (classification.text.equals(name)) { 78 | return classification; 79 | } 80 | } 81 | 82 | return null; 83 | } 84 | 85 | /** 86 | * Parse a json list of strings into a set of mouse gestures. 87 | * 88 | * @param nodes the json list of strings 89 | * @return set of mouse gestures parsed. 90 | */ 91 | public static Set parseJSON(List nodes) { 92 | Set output = new HashSet<>(); 93 | for (JsonNode node : nodes) { 94 | String name = node.getStringValue("name"); 95 | MouseGesture gesture = find(name); 96 | 97 | if (gesture != null) { 98 | output.add(gesture); 99 | } 100 | } 101 | return output; 102 | } 103 | 104 | @Override 105 | public JsonRootNode jsonize() { 106 | return JsonNodeFactories.object(JsonNodeFactories.field("name", JsonNodeFactories.string(text))); 107 | } 108 | } -------------------------------------------------------------------------------- /src/core/config/Parser1_2.java: -------------------------------------------------------------------------------- 1 | package core.config; 2 | 3 | import java.io.File; 4 | import java.util.logging.Level; 5 | import java.util.logging.Logger; 6 | 7 | import utilities.FileUtility; 8 | import utilities.Function; 9 | import utilities.JSONUtility; 10 | import argo.jdom.JsonNode; 11 | import argo.jdom.JsonNodeFactories; 12 | import argo.jdom.JsonRootNode; 13 | import core.languageHandler.Language; 14 | 15 | public class Parser1_2 extends ConfigParser { 16 | 17 | private static final Logger LOGGER = Logger.getLogger(Parser1_2.class.getName()); 18 | 19 | /** 20 | * This is first used with release 1.7.1 21 | */ 22 | @Override 23 | protected String getVersion() { 24 | return "1.2"; 25 | } 26 | 27 | @Override 28 | protected String getPreviousVersion() { 29 | return "1.1"; 30 | } 31 | 32 | @Override 33 | protected JsonRootNode internalConvertFromPreviousVersion(JsonRootNode previousVersion) { 34 | try { 35 | JsonNode taskGroup = JsonNodeFactories.array(new Function(){ 36 | @Override 37 | public JsonNode apply(JsonNode taskGroup) { 38 | return JSONUtility.replaceChild(taskGroup, "tasks", JsonNodeFactories.array( 39 | new Function() { 40 | @Override 41 | public JsonNode apply(JsonNode task) { 42 | String compiler = task.getStringValue("compiler"); 43 | File f = new File(task.getStringValue("source_path")); 44 | File newFile = null; 45 | String newName = f.getName(); 46 | 47 | if (compiler.equals(Language.JAVA.toString())) { 48 | if (!newName.startsWith("CC_")) { 49 | newName = "CC_" + newName; 50 | } 51 | 52 | if (!newName.endsWith(".java")) { 53 | newName += ".java"; 54 | } 55 | } else if (compiler.equals(Language.PYTHON.toString())) { 56 | if (!newName.startsWith("PY_")) { 57 | newName = "PY_" + newName; 58 | } 59 | 60 | if (!newName.endsWith(".py")) { 61 | newName = newName + ".py"; 62 | } 63 | } 64 | newFile = FileUtility.renameFile(f, newName); 65 | 66 | if (FileUtility.fileExists(f)) { 67 | f.renameTo(newFile); 68 | } 69 | 70 | return JSONUtility.replaceChild(task, "source_path", JsonNodeFactories.string(newFile.getAbsolutePath())); 71 | } 72 | }.map(taskGroup.getArrayNode("tasks")))); 73 | } 74 | }.map(previousVersion.getArrayNode("task_groups"))); 75 | 76 | JsonRootNode newRoot = JsonNodeFactories.object( 77 | JsonNodeFactories.field("version", JsonNodeFactories.string(getVersion())), 78 | JsonNodeFactories.field("global_hotkey", previousVersion.getNode("global_hotkey")), 79 | JsonNodeFactories.field("compilers", previousVersion.getNode("compilers")), 80 | JsonNodeFactories.field("task_groups", taskGroup) 81 | ); 82 | 83 | return newRoot; 84 | } catch (Exception e) { 85 | LOGGER.log(Level.WARNING, "Unable to convert json from previous version " + getPreviousVersion(), e); 86 | return null; 87 | } 88 | } 89 | 90 | @Override 91 | protected boolean internalImportData(Config config, JsonRootNode data) { 92 | LOGGER.warning("Unsupported import data at version " + getVersion()); 93 | return false; 94 | } 95 | } -------------------------------------------------------------------------------- /src/frontEnd/IpcBackendHolder.java: -------------------------------------------------------------------------------- 1 | package frontEnd; 2 | 3 | import java.awt.event.MouseEvent; 4 | import java.io.IOException; 5 | import java.util.concurrent.ScheduledFuture; 6 | import java.util.concurrent.TimeUnit; 7 | import java.util.logging.Level; 8 | import java.util.logging.Logger; 9 | 10 | import utilities.ILoggable; 11 | import utilities.swing.SwingUtil; 12 | import core.ipc.IIPCService; 13 | import core.ipc.IPCServiceManager; 14 | 15 | public class IpcBackendHolder implements ILoggable { 16 | private final IpcFrame frame; 17 | private ScheduledFuture scheduled; 18 | 19 | protected IpcBackendHolder(IpcFrame frame) { 20 | this.frame = frame; 21 | } 22 | 23 | protected void startProcess() { 24 | try { 25 | IIPCService selected = getSelectedService(); 26 | if (selected != null) { 27 | selected.startRunning(); 28 | } 29 | } catch (IOException e) { 30 | getLogger().log(Level.WARNING, "Unable to start service...", e); 31 | } 32 | renderServices(); 33 | } 34 | 35 | protected void stopProcess() { 36 | try { 37 | IIPCService selected = getSelectedService(); 38 | if (selected != null) { 39 | selected.stopRunning(); 40 | } 41 | } catch (IOException e) { 42 | getLogger().log(Level.WARNING, "Unable to stop service...", e); 43 | } 44 | renderServices(); 45 | } 46 | 47 | protected void periodicRefresh() { 48 | if (scheduled != null) { 49 | return; 50 | } 51 | 52 | scheduled = frame.mainFrame.executor.scheduleWithFixedDelay(new Runnable() { 53 | @Override 54 | public void run() { 55 | renderServices(); 56 | } 57 | }, 0, 1, TimeUnit.SECONDS); 58 | } 59 | 60 | protected void stopPeriodicRefresh() { 61 | scheduled.cancel(false); 62 | scheduled = null; 63 | } 64 | 65 | protected void renderServices() { 66 | SwingUtil.TableUtil.clearTable(frame.tIpc); 67 | SwingUtil.TableUtil.setRowNumber(frame.tIpc, IPCServiceManager.IPC_SERVICE_COUNT); 68 | for (int i = 0; i < IPCServiceManager.IPC_SERVICE_COUNT; i++) { 69 | IIPCService service = IPCServiceManager.getIPCService(i); 70 | frame.tIpc.setValueAt(service.getName(), i, IpcFrame.COLUMN_NAME); 71 | frame.tIpc.setValueAt(service.getPort(), i, IpcFrame.COLUMN_PORT); 72 | frame.tIpc.setValueAt(service.isRunning(), i, IpcFrame.COLUMN_STATUS); 73 | frame.tIpc.setValueAt(service.isLaunchAtStartup(), i, IpcFrame.COLUMN_LAUCNH_AT_STARTUP); 74 | } 75 | } 76 | 77 | private IIPCService getSelectedService() { 78 | int selected = frame.tIpc.getSelectedRow(); 79 | if (selected < 0 || selected >= IPCServiceManager.IPC_SERVICE_COUNT) { 80 | return null; 81 | } 82 | 83 | IIPCService output = IPCServiceManager.getIPCService(selected); 84 | return output; 85 | } 86 | 87 | protected void mouseReleasedIPCTable(MouseEvent e) { 88 | int col = frame.tIpc.getSelectedColumn(); 89 | 90 | if (col != IpcFrame.COLUMN_LAUCNH_AT_STARTUP) { 91 | return; 92 | } 93 | 94 | IIPCService service = getSelectedService(); 95 | if (service == null) { 96 | return; 97 | } 98 | 99 | service.setLaunchAtStartup(!service.isLaunchAtStartup()); 100 | } 101 | 102 | @Override 103 | public Logger getLogger() { 104 | return Logger.getLogger(IpcBackendHolder.class.getName()); 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/core/ipc/repeatServer/processors/SharedMemoryProcessor.java: -------------------------------------------------------------------------------- 1 | package core.ipc.repeatServer.processors; 2 | 3 | import java.util.List; 4 | 5 | import utilities.Function; 6 | import argo.jdom.JsonNode; 7 | import argo.jdom.JsonNodeFactories; 8 | import core.ipc.repeatServer.MainMessageSender; 9 | import core.userDefinedTask.SharedVariables; 10 | 11 | /** 12 | * This class represents the message processor for any shared memory action. 13 | * 14 | * A received message from the lower layer (central processor) will have the following JSON contents: 15 | * { 16 | * "device": fixed to be "shared_memory" (this may specify types of memory in the future), 17 | * "action" : a string specifying action, 18 | * "parameters" : a list of parameters for this action 19 | * } 20 | * 21 | ************************************************************************* 22 | * The following actions are supported: 23 | * 1) get(string_namespace, string_varname): get value of a variable. 24 | * 2) set(string_namespace, string_varname, string_value): set value of a variable. 25 | * 3) del(string_namespace, string_varname): delete value of a variable. 26 | * 27 | * @author HP Truong 28 | */ 29 | public class SharedMemoryProcessor extends AbstractMessageProcessor { 30 | 31 | private static final String DEVICE_NAME = "shared_memory"; 32 | 33 | protected SharedMemoryProcessor(MainMessageSender messageSender) { 34 | super(messageSender); 35 | } 36 | 37 | @Override 38 | public boolean process(String type, long id, JsonNode content) throws InterruptedException { 39 | String action = content.getStringValue("action"); 40 | List parameterNodes = content.getArrayNode("parameters"); 41 | List params = new Function(){ 42 | @Override 43 | public String apply(JsonNode d) { 44 | return d.getStringValue(); 45 | }}.map(parameterNodes); 46 | 47 | if (action.equals("get")) { 48 | if (params.size() == 2) { 49 | return constructSuccessfulMessage(type, id, SharedVariables.getVar(params.get(0), params.get(1))); 50 | } else { 51 | return failure(type, id, "Invalid parameter length " + params.size()); 52 | } 53 | } else if (action.equals("set")) { 54 | if (params.size() == 3) { 55 | return constructSuccessfulMessage(type, id, SharedVariables.setVar(params.get(0), params.get(1), params.get(2))); 56 | } else { 57 | return failure(type, id, "Invalid parameter length " + params.size()); 58 | } 59 | } else if (action.equals("del")) { 60 | if (params.size() == 2) { 61 | return constructSuccessfulMessage(type, id, SharedVariables.delVar(params.get(0), params.get(1))); 62 | } else { 63 | return failure(type, id, "Invalid parameter length " + params.size()); 64 | } 65 | } 66 | 67 | return failure(type, id, "Unsupported action " + action); 68 | } 69 | 70 | private boolean constructSuccessfulMessage(String type, long id, String result) { 71 | return success(type, id, 72 | result != null ? JsonNodeFactories.string(result) : JsonNodeFactories.nullNode()); 73 | } 74 | 75 | @Override 76 | protected boolean verifyMessageContent(JsonNode content) { 77 | return content.isStringValue("device") && 78 | content.isStringValue("action") && 79 | content.isArrayNode("parameters") && 80 | content.getStringValue("device").equals(DEVICE_NAME); 81 | } 82 | 83 | } 84 | -------------------------------------------------------------------------------- /src/globalListener/GlobalKeyListener.java: -------------------------------------------------------------------------------- 1 | package globalListener; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.logging.Handler; 6 | import java.util.logging.Level; 7 | import java.util.logging.Logger; 8 | 9 | import org.jnativehook.GlobalScreen; 10 | import org.jnativehook.keyboard.NativeKeyEvent; 11 | import org.jnativehook.keyboard.NativeKeyListener; 12 | 13 | import utilities.Function; 14 | 15 | public class GlobalKeyListener implements NativeKeyListener, GlobalListener { 16 | 17 | private static final Logger LOGGER = Logger.getLogger(GlobalKeyListener.class.getName()); 18 | private static final long KEY_PRESS_DELAY_MS = 1000; 19 | 20 | private Function keyPressed; 21 | private Function keyReleased; 22 | private Map m; 23 | 24 | public GlobalKeyListener() { 25 | m = new HashMap(); 26 | 27 | keyPressed = new Function() { 28 | @Override 29 | public Boolean apply(NativeKeyEvent r) { 30 | return true; 31 | } 32 | }; 33 | 34 | keyReleased = new Function() { 35 | @Override 36 | public Boolean apply(NativeKeyEvent r) { 37 | return true; 38 | } 39 | }; 40 | } 41 | 42 | @Override 43 | public boolean startListening() { 44 | GlobalScreen.addNativeKeyListener(this); 45 | return true; 46 | } 47 | 48 | @Override 49 | public boolean stopListening() { 50 | GlobalScreen.removeNativeKeyListener(this); 51 | return true; 52 | } 53 | 54 | @Override 55 | public void nativeKeyPressed(NativeKeyEvent e) { 56 | int code = e.getKeyCode(); 57 | long time = System.currentTimeMillis(); 58 | 59 | if (keyPressed != null) { 60 | if ((!m.containsKey(code)) || (m.get(code) - time >= KEY_PRESS_DELAY_MS)) { 61 | if (!keyPressed.apply(e)) { 62 | LOGGER.warning("Internal key listener problem. Unable to apply key pressed action"); 63 | } 64 | } 65 | } 66 | m.put(code, time); 67 | } 68 | 69 | @Override 70 | public void nativeKeyReleased(NativeKeyEvent e) { 71 | m.remove(e.getKeyCode()); 72 | 73 | if (keyReleased != null) { 74 | if (!keyReleased.apply(e)) { 75 | LOGGER.warning("Internal key listener problem. Unable to apply key released action"); 76 | } 77 | } 78 | } 79 | 80 | @Override 81 | public final void nativeKeyTyped(NativeKeyEvent arg0) { 82 | } 83 | 84 | public void setKeyPressed(Function keyPressed) { 85 | this.keyPressed = keyPressed; 86 | } 87 | 88 | public void setKeyReleased(Function keyReleased) { 89 | this.keyReleased = keyReleased; 90 | } 91 | 92 | public static void main(String[] args) { 93 | // Get the logger for "org.jnativehook" and set the level to off. 94 | Logger logger = Logger.getLogger(GlobalScreen.class.getPackage().getName()); 95 | logger.setLevel(Level.OFF); 96 | 97 | // Change the level for all handlers attached to the default logger. 98 | Handler[] handlers = Logger.getLogger("").getHandlers(); 99 | for (int i = 0; i < handlers.length; i++) { 100 | handlers[i].setLevel(Level.OFF); 101 | } 102 | 103 | GlobalKeyListener listener = new GlobalKeyListener(); 104 | if (listener.startListening()) { 105 | GlobalScreen.addNativeKeyListener(listener); 106 | } 107 | } 108 | } -------------------------------------------------------------------------------- /src/globalListener/GlobalMouseListener.java: -------------------------------------------------------------------------------- 1 | package globalListener; 2 | 3 | import java.util.logging.Logger; 4 | 5 | import org.jnativehook.GlobalScreen; 6 | import org.jnativehook.mouse.NativeMouseEvent; 7 | import org.jnativehook.mouse.NativeMouseInputListener; 8 | 9 | import utilities.Function; 10 | 11 | public class GlobalMouseListener implements NativeMouseInputListener, GlobalListener { 12 | 13 | private static final Logger LOGGER = Logger.getLogger(GlobalMouseListener.class.getName()); 14 | 15 | private Function mousePressed; 16 | private Function mouseReleased; 17 | private Function mouseMoved; 18 | 19 | public GlobalMouseListener() { 20 | mousePressed = new Function() { 21 | @Override 22 | public Boolean apply(NativeMouseEvent r) { 23 | return true; 24 | } 25 | }; 26 | 27 | mouseReleased = new Function() { 28 | @Override 29 | public Boolean apply(NativeMouseEvent r) { 30 | return true; 31 | } 32 | }; 33 | 34 | mouseMoved = new Function() { 35 | @Override 36 | public Boolean apply(NativeMouseEvent r) { 37 | return true; 38 | } 39 | }; 40 | } 41 | 42 | @Override 43 | public void nativeMouseClicked(NativeMouseEvent arg0) { 44 | 45 | } 46 | 47 | @Override 48 | public void nativeMousePressed(NativeMouseEvent arg0) { 49 | if (mousePressed != null) { 50 | if (!mousePressed.apply(arg0)) { 51 | LOGGER.warning("Mouse pressed event callback failed"); 52 | } 53 | } 54 | } 55 | 56 | @Override 57 | public void nativeMouseReleased(NativeMouseEvent arg0) { 58 | if (mouseReleased != null) { 59 | if (!mouseReleased.apply(arg0)) { 60 | LOGGER.warning("Mouse release event callback failed"); 61 | } 62 | } 63 | } 64 | 65 | @Override 66 | public void nativeMouseDragged(NativeMouseEvent arg0) { 67 | } 68 | 69 | @Override 70 | public void nativeMouseMoved(NativeMouseEvent arg0) { 71 | if (mouseMoved != null) { 72 | if (!mouseMoved.apply(arg0)) { 73 | LOGGER.warning("Mouse move event callback failed"); 74 | } 75 | } 76 | } 77 | 78 | public Function getMousePressed() { 79 | return mousePressed; 80 | } 81 | 82 | public void setMousePressed(Function mousePressed) { 83 | this.mousePressed = mousePressed; 84 | } 85 | 86 | public Function getMouseReleased() { 87 | return mouseReleased; 88 | } 89 | 90 | public void setMouseReleased(Function mouseReleased) { 91 | this.mouseReleased = mouseReleased; 92 | } 93 | 94 | public Function getMouseMoved() { 95 | return mouseMoved; 96 | } 97 | 98 | public void setMouseMoved(Function mouseMoved) { 99 | this.mouseMoved = mouseMoved; 100 | } 101 | 102 | @Override 103 | public boolean startListening() { 104 | GlobalScreen.addNativeMouseListener(this); 105 | GlobalScreen.addNativeMouseMotionListener(this); 106 | return true; 107 | } 108 | 109 | @Override 110 | public boolean stopListening() { 111 | GlobalScreen.removeNativeMouseListener(this); 112 | GlobalScreen.removeNativeMouseMotionListener(this); 113 | return true; 114 | } 115 | } 116 | -------------------------------------------------------------------------------- /src/core/ipc/repeatServer/processors/SystemRequestProcessor.java: -------------------------------------------------------------------------------- 1 | package core.ipc.repeatServer.processors; 2 | 3 | import java.util.List; 4 | 5 | import argo.jdom.JsonNode; 6 | import core.ipc.repeatServer.MainMessageSender; 7 | 8 | /** 9 | * This class represents the message processor for any system action. 10 | * 11 | * A received message from the lower layer (central processor) will have the following JSON contents: 12 | * { 13 | * "device": reserved field. Must be empty string, 14 | * "action": action depending on the type parsed by lower layer, 15 | * "parameters": parameters for this action 16 | * } 17 | * 18 | * The possible actions for system_host are: 19 | * 1) Keep alive : keep this connection alive. If this is not received frequently, the system will terminate connection to client 20 | * 21 | * The possible actions for system client are: 22 | * 1) identify(name) : identify the client system as the remote compiler for a certain language. 23 | * 24 | * @author HP Truong 25 | * 26 | */ 27 | public class SystemRequestProcessor extends AbstractMessageProcessor { 28 | 29 | private final ServerMainProcessor holder; 30 | 31 | protected SystemRequestProcessor(MainMessageSender messageSender, ServerMainProcessor holder) { 32 | super(messageSender); 33 | this.holder = holder; 34 | } 35 | 36 | @Override 37 | public boolean process(String type, long id, JsonNode content) { 38 | if (!verifyMessageContent(content)) { 39 | getLogger().warning("Error in verifying message content " + content); 40 | return false; 41 | } 42 | 43 | String device = content.getStringValue("device"); 44 | String action = content.getStringValue("action"); 45 | List paramNodes = content.getArrayNode("parameters"); // Unused 46 | 47 | if (IpcMessageType.identify(type) == IpcMessageType.SYSTEM_HOST) { 48 | if (action.equals("keep_alive")) { 49 | return success(type, id); 50 | } 51 | } else if (IpcMessageType.identify(type) == IpcMessageType.SYSTEM_CLIENT) { 52 | if (action.equals("identify")) { 53 | if (paramNodes.size() != 2) { 54 | getLogger().warning("Unexpected identity to have " + paramNodes.size() + " parameters."); 55 | return false; 56 | } 57 | 58 | String name; 59 | JsonNode nameNode = paramNodes.get(0); 60 | if (!nameNode.isStringValue()) { 61 | getLogger().warning("Identity must be a string."); 62 | return false; 63 | } 64 | 65 | int port; 66 | JsonNode portNode = paramNodes.get(1); 67 | if (!portNode.isStringValue()) { 68 | getLogger().warning("Port number must be a parsable string."); 69 | return false; 70 | } else { 71 | try { 72 | port = Integer.parseInt(portNode.getStringValue()); 73 | } catch (NumberFormatException e) { 74 | getLogger().warning("Port number must be a number."); 75 | return false; 76 | } 77 | } 78 | 79 | name = nameNode.getStringValue(); 80 | TaskProcessorManager.identifyProcessor(name, port, holder.getTaskProcessor()); 81 | return success(type, id); 82 | } 83 | } 84 | 85 | getLogger().warning("Unsupported operation [" + device + ", " + action + "]"); 86 | return false; 87 | } 88 | 89 | @Override 90 | protected boolean verifyMessageContent(JsonNode content) { 91 | return content.isStringValue("device") && 92 | content.getStringValue("device").startsWith("system") && 93 | content.isStringValue("action") && 94 | content.isArrayNode("parameters"); 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/core/userDefinedTask/UsageStatistics.java: -------------------------------------------------------------------------------- 1 | package core.userDefinedTask; 2 | 3 | import java.util.Calendar; 4 | import java.util.logging.Level; 5 | import java.util.logging.Logger; 6 | 7 | import utilities.DateUtility; 8 | import utilities.IJsonable; 9 | import utilities.ILoggable; 10 | import argo.jdom.JsonNode; 11 | import argo.jdom.JsonNodeFactories; 12 | import argo.jdom.JsonRootNode; 13 | 14 | public class UsageStatistics implements IJsonable, ILoggable { 15 | 16 | private static final Logger LOGGER = Logger.getLogger(UsageStatistics.class.getName()); 17 | 18 | private long count; 19 | private Calendar lastUse; 20 | private Calendar created; 21 | private long totalExecutionTime; 22 | 23 | public UsageStatistics() { 24 | created = Calendar.getInstance(); 25 | } 26 | 27 | @Override 28 | public JsonRootNode jsonize() { 29 | return JsonNodeFactories.object( 30 | JsonNodeFactories.field("count", JsonNodeFactories.number(count)), 31 | JsonNodeFactories.field("total_execution_time", JsonNodeFactories.number(totalExecutionTime)), 32 | JsonNodeFactories.field("last_use", lastUse != null ? JsonNodeFactories.string(DateUtility.calendarToTimeString(lastUse)) : JsonNodeFactories.nullNode()), 33 | JsonNodeFactories.field("created", JsonNodeFactories.string(DateUtility.calendarToTimeString(created))) 34 | ); 35 | } 36 | 37 | public static UsageStatistics parseJSON(JsonNode node) { 38 | try { 39 | long count = Long.parseLong(node.getNumberValue("count")); 40 | long totalExecutionTime = Long.parseLong(node.getNumberValue("total_execution_time")); 41 | 42 | Calendar lastUse; 43 | if (node.isNullableObjectNode("last_use")) { 44 | lastUse = null; 45 | } else { 46 | lastUse = DateUtility.stringToCalendar(node.getStringValue("last_use")); 47 | } 48 | 49 | Calendar created = DateUtility.stringToCalendar(node.getStringValue("created")); 50 | 51 | if (created == null) { 52 | LOGGER.warning("Unable to parse object."); 53 | return null; 54 | } 55 | 56 | UsageStatistics output = new UsageStatistics(); 57 | output.count = count; 58 | output.totalExecutionTime = totalExecutionTime; 59 | output.lastUse = lastUse; 60 | output.created = created; 61 | 62 | return output; 63 | } catch (Exception e) { 64 | LOGGER.log(Level.WARNING, "Encountered exception when parsing usage statistics", e); 65 | return null; 66 | } 67 | } 68 | 69 | public long getCount() { 70 | return count; 71 | } 72 | 73 | public Calendar getLastUse() { 74 | return lastUse; 75 | } 76 | 77 | public void useNow() { 78 | if (lastUse == null) { 79 | lastUse = Calendar.getInstance(); 80 | } else { 81 | lastUse.setTimeInMillis(System.currentTimeMillis()); 82 | } 83 | } 84 | 85 | public Calendar getCreated() { 86 | return created; 87 | } 88 | 89 | public void createNow() { 90 | created.setTimeInMillis(System.currentTimeMillis()); 91 | } 92 | 93 | public double getAverageExecutionTime() { 94 | return (double) totalExecutionTime / count; 95 | } 96 | 97 | public long getTotalExecutionTime() { 98 | return totalExecutionTime; 99 | } 100 | 101 | public void updateAverageExecutionTime(long newExecutionTime) { 102 | count++; 103 | totalExecutionTime += newExecutionTime; 104 | } 105 | 106 | @Override 107 | public Logger getLogger() { 108 | return Logger.getLogger(UsageStatistics.class.getName()); 109 | } 110 | } 111 | -------------------------------------------------------------------------------- /src/core/userDefinedTask/TaskGroup.java: -------------------------------------------------------------------------------- 1 | package core.userDefinedTask; 2 | 3 | import java.util.ArrayList; 4 | import java.util.List; 5 | import java.util.Set; 6 | import java.util.logging.Level; 7 | import java.util.logging.Logger; 8 | 9 | import utilities.IJsonable; 10 | import argo.jdom.JsonNode; 11 | import argo.jdom.JsonNodeFactories; 12 | import argo.jdom.JsonRootNode; 13 | import core.keyChain.GlobalEventsManager; 14 | import core.languageHandler.compiler.DynamicCompilerManager; 15 | 16 | public class TaskGroup implements IJsonable { 17 | 18 | private String name; 19 | private boolean enabled; 20 | private final List tasks; 21 | 22 | public TaskGroup(String name, List tasks) { 23 | this.name = name; 24 | this.tasks = tasks; 25 | this.enabled = true; 26 | } 27 | 28 | public TaskGroup(String name) { 29 | this(name, new ArrayList()); 30 | } 31 | 32 | public List getTasks() { 33 | return tasks; 34 | } 35 | 36 | public String getName() { 37 | return name; 38 | } 39 | 40 | public void setName(String name) { 41 | this.name = name; 42 | } 43 | 44 | public boolean isEnabled() { 45 | return enabled; 46 | } 47 | 48 | private void setEnabled(boolean enabled) { 49 | this.enabled = enabled; 50 | } 51 | 52 | public void setEnabled(boolean enabled, GlobalEventsManager keyManager) { 53 | if (keyManager == null) { 54 | return; 55 | } else { 56 | if (enabled) { 57 | for (UserDefinedAction task : tasks) { 58 | if (task.isEnabled()) { 59 | Set collisions = keyManager.isTaskRegistered(task); 60 | if (collisions.isEmpty()) { 61 | keyManager.registerTask(task); 62 | } else { // Revert everything and exit 63 | unregisterAll(keyManager); 64 | GlobalEventsManager.showCollisionWarning(null, collisions); 65 | return; 66 | } 67 | } 68 | } 69 | } else { 70 | unregisterAll(keyManager); 71 | } 72 | setEnabled(enabled); 73 | } 74 | } 75 | 76 | private void unregisterAll(GlobalEventsManager keyManager) { 77 | for (UserDefinedAction task : tasks) { 78 | keyManager.unregisterTask(task); 79 | } 80 | } 81 | 82 | @Override 83 | public JsonRootNode jsonize() { 84 | List taskNodes = new ArrayList<>(); 85 | for (UserDefinedAction task : tasks) { 86 | taskNodes.add(task.jsonize()); 87 | } 88 | 89 | return JsonNodeFactories.object( 90 | JsonNodeFactories.field("name", JsonNodeFactories.string(name)), 91 | JsonNodeFactories.field("enabled", JsonNodeFactories.booleanNode(enabled)), 92 | JsonNodeFactories.field("tasks", JsonNodeFactories.array(taskNodes)) 93 | ); 94 | } 95 | 96 | public static TaskGroup parseJSON(DynamicCompilerManager factory, JsonNode node) { 97 | try { 98 | TaskGroup output = new TaskGroup(""); 99 | String name = node.getStringValue("name"); 100 | output.name = name; 101 | 102 | for (JsonNode task : node.getArrayNode("tasks")) { 103 | UserDefinedAction action = UserDefinedAction.parseJSON(factory, task); 104 | if (action != null) { 105 | output.tasks.add(action); 106 | } 107 | } 108 | 109 | output.enabled = node.getBooleanValue("enabled"); 110 | 111 | return output; 112 | } catch (Exception e) { 113 | Logger.getLogger(TaskGroup.class.getName()).log(Level.WARNING, "Exception parsing task group from JSON", e); 114 | return null; 115 | } 116 | } 117 | } 118 | -------------------------------------------------------------------------------- /src/core/recorder/TaskScheduler.java: -------------------------------------------------------------------------------- 1 | package core.recorder; 2 | 3 | import java.util.logging.Level; 4 | import java.util.logging.Logger; 5 | 6 | import utilities.Function; 7 | import core.scheduler.AbstractScheduler; 8 | import core.scheduler.SchedulingData; 9 | 10 | class TaskScheduler extends AbstractScheduler { 11 | 12 | private static final Logger LOGGER = Logger.getLogger(TaskScheduler.class.getName()); 13 | 14 | static { 15 | LOGGER.setLevel(Level.ALL); 16 | } 17 | 18 | private Thread executeAgent; 19 | private boolean isRunning; 20 | 21 | protected TaskScheduler() { 22 | super(); 23 | } 24 | 25 | protected synchronized long runTasks(final long count, final long delay, final Function callBack, final long callBackDelay) { 26 | if (isRunning) { 27 | LOGGER.info("Cannot invoke two running instances"); 28 | return 0; 29 | } else if (count < 1) { 30 | LOGGER.warning("Attempt to run tasks with count " + count); 31 | return 0; 32 | } else if (delay < 0) { 33 | LOGGER.warning("Attempt to run tasks with negative delay " + delay); 34 | return 0; 35 | } else if (callBackDelay < 0) { 36 | LOGGER.warning("Attempt to run tasks with negative callBack delay " + callBackDelay); 37 | return 0; 38 | } 39 | 40 | isRunning = true; 41 | Runnable running = new Runnable() { 42 | @Override 43 | public void run() { 44 | for (long i = 0; i < count; i++) { 45 | long time = 0; 46 | for (SchedulingData t : tasks) { 47 | long currentTime = t.getTime(); 48 | 49 | if (currentTime < time) { 50 | LOGGER.severe("Something went really bad"); 51 | System.exit(1); 52 | } 53 | 54 | try { 55 | Thread.sleep(currentTime - time); 56 | } catch (InterruptedException e) { 57 | LOGGER.info("Ended prematuredly"); 58 | return; // Ended prematurely 59 | } 60 | 61 | time = currentTime; 62 | t.getData().run(); 63 | } 64 | 65 | if (delay > 0) { 66 | try { 67 | Thread.sleep(delay); 68 | } catch (InterruptedException e) { 69 | LOGGER.info("Ended prematuredly"); 70 | return; // Ended prematurely 71 | } 72 | } 73 | } 74 | 75 | if (callBack != null && callBackDelay > 0) { 76 | try { 77 | Thread.sleep(callBackDelay); 78 | } catch (InterruptedException e) { 79 | return; // Ended prematurely 80 | } 81 | callBack.apply(null); 82 | } 83 | } 84 | }; 85 | executeAgent = new Thread(running); 86 | executeAgent.start(); 87 | 88 | if (tasks.isEmpty()) { 89 | LOGGER.info("Nothing to run"); 90 | return callBackDelay; 91 | } else { 92 | return tasks.getLast().getTime() + callBackDelay; 93 | } 94 | } 95 | 96 | protected synchronized void halt() { 97 | if (isRunning) { 98 | if (Thread.currentThread() != executeAgent) { 99 | while (executeAgent.isAlive()) { 100 | executeAgent.interrupt(); 101 | } 102 | } 103 | 104 | isRunning = false; 105 | } else { 106 | LOGGER.warning("Failed attempting to halt scheduler while not running!"); 107 | } 108 | } 109 | 110 | protected synchronized boolean clearTasks() { 111 | if (isRunning) { 112 | LOGGER.info("Stop task scheduler first before clearing tasks"); 113 | return false; 114 | } 115 | 116 | tasks.clear(); 117 | return true; 118 | } 119 | 120 | @Override 121 | protected synchronized boolean isLegalAddTask() { 122 | if (isRunning) { 123 | LOGGER.warning("Failed attempt to add task to scheduler while running."); 124 | return false; 125 | } 126 | return true; 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /src/core/config/ConfigParser.java: -------------------------------------------------------------------------------- 1 | package core.config; 2 | 3 | import java.util.logging.Level; 4 | import java.util.logging.Logger; 5 | 6 | import utilities.JSONUtility; 7 | import argo.jdom.JsonNode; 8 | import argo.jdom.JsonNodeFactories; 9 | import argo.jdom.JsonRootNode; 10 | 11 | abstract class ConfigParser { 12 | 13 | private static final Logger LOGGER = Logger.getLogger(ConfigParser.class.getName()); 14 | 15 | protected abstract String getVersion(); 16 | protected abstract String getPreviousVersion(); 17 | 18 | protected final JsonRootNode convertFromPreviousVersion(JsonRootNode previousVersion) { 19 | if (previousVersion != null && previousVersion.isStringValue("version")) { 20 | if (previousVersion.getStringValue("version").equals(getPreviousVersion())) { 21 | JsonRootNode output = internalConvertFromPreviousVersion(previousVersion); 22 | 23 | try { 24 | JsonNode convertedVersion = JSONUtility.replaceChild(output, "version", JsonNodeFactories.string(getVersion())); 25 | return convertedVersion.getRootNode(); 26 | } catch (Exception e) { 27 | LOGGER.log(Level.WARNING, "Unable to modify version when converting versions " + getPreviousVersion() + " to " + getVersion(), e); 28 | return null; 29 | } 30 | } 31 | } 32 | LOGGER.warning("Invalid previous version " + getPreviousVersion() + " cannot " 33 | + "be converted to this version " + getVersion() + ". Only accept previous version " + getPreviousVersion()); 34 | return null; 35 | } 36 | 37 | protected abstract JsonRootNode internalConvertFromPreviousVersion(JsonRootNode previousVersion); 38 | 39 | protected final boolean extractData(Config config, JsonRootNode data) { 40 | try { 41 | // Sanity check 42 | if (!data.getStringValue("version").equals(getVersion())) { 43 | LOGGER.warning("Invalid version " + data.getStringValue("version") + " with parser of version " 44 | + getVersion()); 45 | return false; 46 | } 47 | } catch (Exception e) { 48 | LOGGER.log(Level.WARNING, "Cannot parse json", e); 49 | return false; 50 | } 51 | 52 | if (!Config.CURRENT_CONFIG_VERSION.equals(getVersion())) { // Then convert to latest version then parse 53 | LOGGER.info("Looking for next version " + getVersion()); 54 | String currentVersion = getVersion(); 55 | while (!currentVersion.equals(Config.CURRENT_CONFIG_VERSION)) { 56 | ConfigParser nextVersion = Config.getNextConfigParser(currentVersion); 57 | 58 | if (nextVersion == null) { 59 | LOGGER.warning("Unable to find the next version of current version " + currentVersion); 60 | return false; 61 | } 62 | 63 | try { 64 | data = nextVersion.convertFromPreviousVersion(data); 65 | } catch (Exception e) { 66 | LOGGER.log(Level.WARNING, "Unable to convert from version " + currentVersion, e); 67 | data = null; 68 | } 69 | 70 | if (data == null) { 71 | LOGGER.log(Level.WARNING, "Unable to convert to later version " + nextVersion.getVersion()); 72 | return false; 73 | } 74 | currentVersion = nextVersion.getVersion(); 75 | } 76 | 77 | ConfigParser parser = Config.getConfigParser(currentVersion); 78 | return parser.extractData(config, data); 79 | } else { 80 | return internalExtractData(config, data); 81 | } 82 | } 83 | 84 | protected boolean internalExtractData(Config config, JsonRootNode data) { 85 | throw new UnsupportedOperationException("Config version " + getVersion() + " does not support extracting data. " 86 | + "Convert to a later version."); 87 | } 88 | 89 | protected final boolean importData(Config config, JsonRootNode data) { 90 | return internalImportData(config, data); 91 | } 92 | 93 | protected abstract boolean internalImportData(Config config, JsonRootNode data); 94 | } -------------------------------------------------------------------------------- /src/core/languageHandler/compiler/CSharpRemoteCompiler.java: -------------------------------------------------------------------------------- 1 | package core.languageHandler.compiler; 2 | 3 | import java.io.File; 4 | import java.util.logging.Level; 5 | import java.util.logging.Logger; 6 | 7 | import javax.swing.JButton; 8 | import javax.swing.JFrame; 9 | import javax.swing.JOptionPane; 10 | 11 | import utilities.FileUtility; 12 | import utilities.Pair; 13 | import argo.jdom.JsonNode; 14 | import argo.jdom.JsonNodeFactories; 15 | import core.controller.Core; 16 | import core.languageHandler.Language; 17 | import core.userDefinedTask.UserDefinedAction; 18 | 19 | public class CSharpRemoteCompiler extends AbstractRemoteNativeCompiler { 20 | 21 | private final File objectFileDirectory; 22 | private File path; 23 | 24 | { 25 | getLogger().setLevel(Level.ALL); 26 | } 27 | 28 | public CSharpRemoteCompiler(File objectFileDirectory) { 29 | this.objectFileDirectory = objectFileDirectory; 30 | path = new File("."); 31 | } 32 | 33 | @Override 34 | public void setPath(File file) { 35 | this.path = file; 36 | } 37 | 38 | @Override 39 | public File getPath() { 40 | return path; 41 | } 42 | 43 | @Override 44 | protected Pair loadAction(final int id, final String source, final File sourceFile) { 45 | UserDefinedAction output = new UserDefinedAction() { 46 | @Override 47 | public void action(Core controller) { 48 | boolean result = remoteTaskManager.runTask(id, invoker); 49 | if (!result) { 50 | getLogger().warning("Unable to run task with id = " + id); 51 | } 52 | } 53 | 54 | @Override 55 | public UserDefinedAction recompile(AbstractNativeCompiler compiler, boolean clean) { 56 | Pair recompiled = CSharpRemoteCompiler.this.compile(source); 57 | UserDefinedAction output = recompiled.getB(); 58 | output.syncContent(this); 59 | return output; 60 | } 61 | }; 62 | output.setSourcePath(sourceFile.getAbsolutePath()); 63 | 64 | getLogger().info("Successfully loaded action from remote compiler with id = " + id); 65 | return new Pair(DynamicCompilerOutput.COMPILATION_SUCCESS, output); 66 | } 67 | 68 | @Override 69 | protected File getSourceFile(String compilingAction) { 70 | return new File(FileUtility.joinPath(objectFileDirectory.getAbsolutePath(), compilingAction + this.getExtension())); 71 | } 72 | 73 | @Override 74 | public Language getName() { 75 | return Language.CSHARP; 76 | } 77 | 78 | @Override 79 | public String getExtension() { 80 | return ".cs"; 81 | } 82 | 83 | @Override 84 | public String getObjectExtension() { 85 | return ".dll"; 86 | } 87 | 88 | @Override 89 | public boolean parseCompilerSpecificArgs(JsonNode node) { 90 | return true; 91 | } 92 | 93 | @Override 94 | public JsonNode getCompilerSpecificArgs() { 95 | return JsonNodeFactories.object(); 96 | } 97 | 98 | @Override 99 | protected String getDummyPrefix() { 100 | return "CS_"; 101 | } 102 | 103 | @Override 104 | public Logger getLogger() { 105 | return Logger.getLogger(CSharpRemoteCompiler.class.getName()); 106 | } 107 | 108 | @Override 109 | protected boolean checkRemoteCompilerSettings() { 110 | return true; 111 | } 112 | 113 | /*******************************************************************/ 114 | /************************Swing components***************************/ 115 | /*******************************************************************/ 116 | 117 | @Override 118 | public void promptChangePath(JFrame parent) { 119 | JOptionPane.showMessageDialog(parent, "Operation not supported", "C# path", JOptionPane.INFORMATION_MESSAGE); 120 | } 121 | 122 | @Override 123 | public void changeCompilationButton(JButton bCompile) { 124 | bCompile.setText("Compile source"); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/core/keyChain/KeyChain.java: -------------------------------------------------------------------------------- 1 | package core.keyChain; 2 | 3 | import java.awt.event.KeyEvent; 4 | import java.util.ArrayList; 5 | import java.util.Arrays; 6 | import java.util.Collections; 7 | import java.util.List; 8 | import java.util.logging.Level; 9 | import java.util.logging.Logger; 10 | 11 | import utilities.Function; 12 | import utilities.IJsonable; 13 | import utilities.StringUtilities; 14 | import argo.jdom.JsonNode; 15 | import argo.jdom.JsonNodeFactories; 16 | import argo.jdom.JsonRootNode; 17 | import core.config.Parser1_0; 18 | 19 | public class KeyChain implements IJsonable { 20 | 21 | private static final Logger LOGGER = Logger.getLogger(Parser1_0.class.getName()); 22 | private final List keys; 23 | 24 | public KeyChain(List keys) { 25 | this.keys = keys; 26 | } 27 | 28 | public KeyChain(int key) { 29 | this(Arrays.asList(key)); 30 | } 31 | 32 | public KeyChain() { 33 | this(new ArrayList()); 34 | } 35 | 36 | /** 37 | * Check if two KeyChain will collide when applied. Formally, 38 | * return true if triggering one KeyChain forces the other to be triggered. To trigger a KeyChain 39 | * is to press all the keys in this.keys in the given order, without releasing any key in the process. 40 | * 41 | * For example, 42 | * A + S + D collides with A + S, but not with S + D or D + S 43 | * Ctrl + Shift + C does not collide with Ctrl + C 44 | * 45 | * @param other other KeyChain to check for collision. 46 | * @return true if this key chain collides with the other key chain 47 | */ 48 | public boolean collideWith(KeyChain other) { 49 | if (keys.size() > other.keys.size()) { 50 | return Collections.indexOfSubList(keys, other.keys) == 0; 51 | } else { 52 | return Collections.indexOfSubList(other.keys, keys) == 0; 53 | } 54 | } 55 | 56 | public List getKeys() { 57 | return keys; 58 | } 59 | 60 | @Override 61 | public String toString() { 62 | return StringUtilities.join(new Function() { 63 | @Override 64 | public String apply(Integer r) { 65 | return KeyEvent.getKeyText(r); 66 | } 67 | }.map(keys), " + "); 68 | } 69 | 70 | @Override 71 | public KeyChain clone() { 72 | return new KeyChain(new ArrayList<>(this.keys)); 73 | } 74 | 75 | @Override 76 | public int hashCode() { 77 | return this.toString().hashCode(); 78 | } 79 | 80 | @Override 81 | public boolean equals(Object obj) { 82 | if (this == obj) { 83 | return true; 84 | } 85 | if (obj == null) { 86 | return false; 87 | } 88 | if (getClass() != obj.getClass()) { 89 | return false; 90 | } 91 | KeyChain other = (KeyChain) obj; 92 | if (keys == null) { 93 | if (other.keys != null) { 94 | return false; 95 | } 96 | } else { 97 | if (this.keys.size() != other.keys.size()) { 98 | return false; 99 | } 100 | for (int i = 0; i < this.keys.size(); i++) { 101 | if (!this.keys.get(i).equals(other.keys.get(i))) { 102 | return false; 103 | } 104 | } 105 | } 106 | return true; 107 | } 108 | 109 | @Override 110 | public JsonRootNode jsonize() { 111 | List keys = this.keys == null ? new ArrayList() : this.keys; 112 | 113 | List hotkeyChain = new Function() { 114 | @Override 115 | public JsonNode apply(Integer r) { 116 | return JsonNodeFactories.number(r); 117 | } 118 | }.map(keys); 119 | 120 | return JsonNodeFactories.array(hotkeyChain); 121 | } 122 | 123 | public static KeyChain parseJSON(List list) { 124 | try { 125 | return new KeyChain(new Function() { 126 | @Override 127 | public Integer apply(JsonNode d) { 128 | return Integer.parseInt(d.getText()); 129 | } 130 | }.map(list)); 131 | } catch (Exception e) { 132 | LOGGER.log(Level.WARNING, "Unable to parse KeyChain", e); 133 | return null; 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /src/core/languageHandler/compiler/AbstractRemoteNativeCompiler.java: -------------------------------------------------------------------------------- 1 | package core.languageHandler.compiler; 2 | 3 | import java.io.File; 4 | import java.util.logging.Level; 5 | 6 | import utilities.FileUtility; 7 | import utilities.Pair; 8 | import utilities.RandomUtil; 9 | import argo.jdom.JsonNode; 10 | import core.ipc.repeatServer.processors.TaskProcessor; 11 | import core.ipc.repeatServer.processors.TaskProcessorManager; 12 | import core.languageHandler.Language; 13 | import core.userDefinedTask.DormantUserDefinedTask; 14 | import core.userDefinedTask.UserDefinedAction; 15 | 16 | public abstract class AbstractRemoteNativeCompiler extends AbstractNativeCompiler { 17 | 18 | protected TaskProcessor remoteTaskManager; 19 | 20 | { 21 | getLogger().setLevel(Level.ALL); 22 | } 23 | 24 | @Override 25 | public final Pair compile(String source) { 26 | String fileName = getDummyPrefix() + RandomUtil.randomID(); 27 | File sourceFile = getSourceFile(fileName); 28 | return compile(source, sourceFile); 29 | } 30 | 31 | @Override 32 | public final Pair compile(String source, File sourceFile) { 33 | TaskProcessor remoteManager = TaskProcessorManager.getProcessor(getName()); 34 | if (remoteManager == null) { 35 | getLogger().warning("Does not have a remote compiler to work with " + getName()); 36 | return new Pair(DynamicCompilerOutput.COMPILER_MISSING, new DormantUserDefinedTask(source)); 37 | } 38 | 39 | remoteTaskManager = remoteManager; 40 | 41 | try { 42 | if (!checkRemoteCompilerSettings()) { 43 | getLogger().warning("Remote compiler check failed! Compilation ended prematurely."); 44 | return new Pair(DynamicCompilerOutput.COMPILER_MISCONFIGURED, new DormantUserDefinedTask(source)); 45 | } 46 | 47 | if (!sourceFile.getName().endsWith(getExtension())) { 48 | getLogger().warning("Source file " + sourceFile.getAbsolutePath() + " does not end with " + getExtension() + ". Compiling from source code."); 49 | return compile(source); 50 | } 51 | 52 | if (!FileUtility.fileExists(sourceFile)) { 53 | if (!FileUtility.writeToFile(source, sourceFile, false)) { 54 | getLogger().warning("Cannot write source code to file " + sourceFile.getAbsolutePath()); 55 | return new Pair(DynamicCompilerOutput.SOURCE_NOT_ACCESSIBLE, new DormantUserDefinedTask(source)); 56 | } 57 | } 58 | 59 | int id = remoteTaskManager.createTask(sourceFile); 60 | if (id == -1) { 61 | getLogger().warning("Unable to create task from ipc client..."); 62 | return new Pair(DynamicCompilerOutput.COMPILATION_ERROR, null); 63 | } 64 | return loadAction(id, source, sourceFile); 65 | } catch (Exception e) { 66 | getLogger().log(Level.WARNING, "Cannot compile source code...", e); 67 | return new Pair(DynamicCompilerOutput.COMPILATION_ERROR, null); 68 | } 69 | } 70 | 71 | protected abstract boolean checkRemoteCompilerSettings(); 72 | 73 | protected abstract Pair loadAction(int id, String source, File objectFile); 74 | 75 | @Override 76 | public abstract Language getName(); 77 | 78 | @Override 79 | public abstract String getExtension(); 80 | 81 | @Override 82 | public abstract String getObjectExtension(); 83 | 84 | @Override 85 | public abstract File getPath(); 86 | 87 | @Override 88 | public abstract void setPath(File path); 89 | 90 | @Override 91 | public abstract boolean parseCompilerSpecificArgs(JsonNode node); 92 | 93 | @Override 94 | public abstract JsonNode getCompilerSpecificArgs(); 95 | 96 | @Override 97 | protected abstract File getSourceFile(String compilingAction); 98 | 99 | @Override 100 | protected abstract String getDummyPrefix(); 101 | } 102 | -------------------------------------------------------------------------------- /src/utilities/swing/LinedTextArea.java: -------------------------------------------------------------------------------- 1 | package utilities.swing; 2 | 3 | import java.awt.Color; 4 | import java.awt.Toolkit; 5 | import java.awt.event.ActionEvent; 6 | import java.awt.event.KeyEvent; 7 | import java.util.logging.Level; 8 | import java.util.logging.Logger; 9 | 10 | import javax.swing.AbstractAction; 11 | import javax.swing.JScrollPane; 12 | import javax.swing.JTextArea; 13 | import javax.swing.KeyStroke; 14 | import javax.swing.event.DocumentEvent; 15 | import javax.swing.event.DocumentListener; 16 | import javax.swing.event.UndoableEditEvent; 17 | import javax.swing.event.UndoableEditListener; 18 | import javax.swing.text.Document; 19 | import javax.swing.text.Element; 20 | import javax.swing.undo.CannotRedoException; 21 | import javax.swing.undo.CannotUndoException; 22 | import javax.swing.undo.UndoManager; 23 | 24 | @SuppressWarnings("serial") 25 | public class LinedTextArea extends JScrollPane { 26 | 27 | private static final Logger LOGGER = Logger.getLogger(LinedTextArea.class.getName()); 28 | private static final int MASK = Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(); 29 | private final JTextArea lines; 30 | 31 | public LinedTextArea(final JTextArea textArea) { 32 | lines = new JTextArea("1"); 33 | lines.setBackground(Color.LIGHT_GRAY); 34 | lines.setEditable(false); 35 | 36 | final UndoManager undoManager = new UndoManager(); 37 | Document document = textArea.getDocument(); 38 | document.addUndoableEditListener(new UndoableEditListener() { 39 | @Override 40 | public void undoableEditHappened(UndoableEditEvent e) { 41 | undoManager.addEdit(e.getEdit()); 42 | // System.out.println(e); 43 | } 44 | }); 45 | textArea.getActionMap().put("Undo", new AbstractAction("Undo") { 46 | @Override 47 | public void actionPerformed(ActionEvent evt) { 48 | try { 49 | if (undoManager.canUndo()) { 50 | undoManager.undo(); 51 | } 52 | } catch (CannotUndoException e) { 53 | LOGGER.log(Level.WARNING, "Unable to undo", e); 54 | } 55 | } 56 | }); 57 | textArea.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_Z, MASK), "Undo"); 58 | textArea.getActionMap().put("Redo", new AbstractAction("Redo") { 59 | @Override 60 | public void actionPerformed(ActionEvent evt) { 61 | try { 62 | if (undoManager.canRedo()) { 63 | undoManager.redo(); 64 | } 65 | } catch (CannotRedoException e) { 66 | LOGGER.log(Level.WARNING, "Unable to redo", e); 67 | } 68 | } 69 | }); 70 | textArea.getInputMap().put(KeyStroke.getKeyStroke(KeyEvent.VK_Y,MASK), "Redo"); 71 | 72 | textArea.getDocument().addDocumentListener(new DocumentListener(){ 73 | private String getText() { 74 | int caretPosition = textArea.getDocument().getLength(); 75 | Element root = textArea.getDocument().getDefaultRootElement(); 76 | StringBuilder text = new StringBuilder("1" + System.getProperty("line.separator")); 77 | for(int i = 2; i < root.getElementIndex(caretPosition) + 2; i++){ 78 | text.append(i + System.getProperty("line.separator")); 79 | } 80 | return text.toString(); 81 | } 82 | @Override 83 | public void changedUpdate(DocumentEvent de) { 84 | lines.setText(getText()); 85 | } 86 | 87 | @Override 88 | public void insertUpdate(DocumentEvent de) { 89 | lines.setText(getText()); 90 | } 91 | 92 | @Override 93 | public void removeUpdate(DocumentEvent de) { 94 | lines.setText(getText()); 95 | } 96 | 97 | }); 98 | 99 | getViewport().add(textArea); 100 | setRowHeaderView(lines); 101 | setVerticalScrollBarPolicy(JScrollPane.VERTICAL_SCROLLBAR_ALWAYS); 102 | } 103 | 104 | 105 | } 106 | -------------------------------------------------------------------------------- /src/core/ipc/repeatServer/ControllerServer.java: -------------------------------------------------------------------------------- 1 | package core.ipc.repeatServer; 2 | 3 | import java.io.IOException; 4 | import java.net.ServerSocket; 5 | import java.net.Socket; 6 | import java.net.SocketException; 7 | import java.util.LinkedList; 8 | import java.util.concurrent.ScheduledThreadPoolExecutor; 9 | import java.util.logging.Level; 10 | import java.util.logging.Logger; 11 | 12 | import core.controller.Core; 13 | import core.ipc.IIPCService; 14 | 15 | public class ControllerServer extends IIPCService { 16 | 17 | private static final int DEFAULT_PORT = 9999; 18 | private static final int DEFAULT_TIMEOUT_MS = 10000; 19 | private static final int MAX_THREAD_COUNT = 10; 20 | 21 | private boolean isStopped; 22 | private final ScheduledThreadPoolExecutor threadPool; 23 | private final LinkedList clientServingThreads; 24 | private ServerSocket listener; 25 | private Thread mainThread; 26 | 27 | public ControllerServer() { 28 | threadPool = new ScheduledThreadPoolExecutor(MAX_THREAD_COUNT); 29 | clientServingThreads = new LinkedList<>(); 30 | this.setPort(DEFAULT_PORT); 31 | } 32 | 33 | @Override 34 | protected void start() throws IOException { 35 | setStop(false); 36 | 37 | mainThread = new Thread() { 38 | @Override 39 | public void run() { 40 | try { 41 | listener = new ServerSocket(port); 42 | } catch (IOException e) { 43 | getLogger().log(Level.SEVERE, "IO Exception when starting server", e); 44 | return; 45 | } 46 | 47 | try { 48 | getLogger().info("Waiting for client connections..."); 49 | while (!isStopped()) { 50 | final Socket socket; 51 | try { 52 | socket = listener.accept(); 53 | socket.setSoTimeout(DEFAULT_TIMEOUT_MS); 54 | getLogger().info("New client accepted: " + socket.getInetAddress().getHostAddress() + ":" + socket.getPort()); 55 | } catch (SocketException e) { 56 | if (!listener.isClosed()) { 57 | getLogger().log(Level.SEVERE, "Socket exception when serving", e); 58 | } 59 | continue; 60 | } catch (IOException e) { 61 | getLogger().log(Level.SEVERE, "IO Exception when serving", e); 62 | continue; 63 | } 64 | 65 | ClientServingThread newClient = new ClientServingThread(Core.getInstance(), socket); 66 | clientServingThreads.add(newClient); 67 | threadPool.submit(newClient); 68 | } 69 | } finally { 70 | try { 71 | listener.close(); 72 | } catch (IOException e) { 73 | getLogger().log(Level.SEVERE, "IO Exception when closing server", e); 74 | } 75 | } 76 | 77 | getLogger().log(Level.INFO, "Controller server terminating..."); 78 | for (ClientServingThread clientThread : clientServingThreads) { 79 | clientThread.stop(); 80 | } 81 | clientServingThreads.clear(); 82 | getLogger().log(Level.INFO, "Controller server terminated!"); 83 | } 84 | }; 85 | mainThread.start(); 86 | } 87 | 88 | @Override 89 | public void stop() { 90 | setStop(true); 91 | if (listener != null) { 92 | try { 93 | listener.close(); 94 | } catch (IOException e) { 95 | getLogger().log(Level.SEVERE, "Failed to close server socket", e); 96 | } 97 | } 98 | } 99 | 100 | private synchronized boolean isStopped() { 101 | return isStopped; 102 | } 103 | 104 | private synchronized void setStop(boolean isStopped) { 105 | this.isStopped = isStopped; 106 | } 107 | 108 | @Override 109 | public boolean isRunning() { 110 | return mainThread != null && mainThread.isAlive(); 111 | } 112 | 113 | @Override 114 | public String getName() { 115 | return "Controller server"; 116 | } 117 | 118 | @Override 119 | public Logger getLogger() { 120 | return Logger.getLogger(ControllerServer.class.getName()); 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /src/core/ipc/repeatClient/IPCClientService.java: -------------------------------------------------------------------------------- 1 | package core.ipc.repeatClient; 2 | 3 | import java.io.BufferedReader; 4 | import java.io.File; 5 | import java.io.IOException; 6 | import java.io.InputStreamReader; 7 | import java.util.logging.Level; 8 | 9 | import utilities.StringUtilities; 10 | 11 | import com.sun.istack.internal.logging.Logger; 12 | 13 | import core.ipc.IIPCService; 14 | 15 | public abstract class IPCClientService extends IIPCService { 16 | 17 | private static final Logger LOGGER = Logger.getLogger(IPCClientService.class); 18 | protected static final long TIMEOUT_MS = 5000; 19 | 20 | protected File executingProgram; // The program used to execute this ipc client 21 | protected Thread mainThread, forceDestroyThread; 22 | protected Process mainProcess; 23 | protected BufferedReader input; 24 | 25 | public void setExecutingProgram(File executablePath) { 26 | if (!executablePath.canExecute()) { 27 | LOGGER.warning("File is not executable"); 28 | } 29 | 30 | this.executingProgram = executablePath; 31 | } 32 | 33 | @Override 34 | public final void start() throws IOException { 35 | if (!executingProgram.exists()) { 36 | getLogger().warning("Launcher " + executingProgram.getAbsolutePath() + " does not exist."); 37 | return; 38 | } 39 | 40 | if (!executingProgram.canExecute()) { 41 | getLogger().warning("Launcher " + executingProgram.getAbsolutePath() + " is not executable."); 42 | return; 43 | } 44 | 45 | final String[] cmd = getLaunchCmd(); 46 | if (cmd == null) { 47 | getLogger().warning("Unable to retrieve launch cmd for " + getName()); 48 | return; 49 | } else { 50 | getLogger().info("Executing " + StringUtilities.join(cmd, " ")); 51 | } 52 | 53 | mainThread = new Thread() { 54 | @Override 55 | public void run() { 56 | try { 57 | String line; 58 | ProcessBuilder processBuilder = new ProcessBuilder(cmd); 59 | processBuilder.redirectErrorStream(true); 60 | mainProcess = processBuilder.start(); 61 | 62 | input = new BufferedReader(new InputStreamReader(mainProcess.getInputStream())); 63 | 64 | while ((line = input.readLine()) != null) { 65 | String trimmed = line.trim(); 66 | if (trimmed.length() == 0) { 67 | continue; 68 | } 69 | 70 | getLogger().info(trimmed); 71 | } 72 | 73 | mainProcess.waitFor(); 74 | } catch (Exception e) { 75 | getLogger().log(Level.WARNING, "Encounter exception while running process: " + cmd[0] + cmd[1], e); 76 | } finally { 77 | try { 78 | input.close(); 79 | } catch (IOException e) { 80 | getLogger().log(Level.WARNING, "Failed to close input stream for " + getName(), e); 81 | } 82 | } 83 | } 84 | }; 85 | 86 | mainThread.start(); 87 | } 88 | 89 | @Override 90 | public final void stop() throws IOException { 91 | if (forceDestroyThread != null) { 92 | getLogger().info("Waiting for " + getName() + " to terminate..."); 93 | return; 94 | } 95 | 96 | forceDestroyThread = new Thread() { 97 | @Override 98 | public void run() { 99 | mainProcess.destroy(); 100 | getLogger().info("Destroyed"); 101 | 102 | try { 103 | Thread.sleep(TIMEOUT_MS); 104 | } catch (InterruptedException e) { 105 | getLogger().log(Level.WARNING, "Interrupted while waiting for " + getName() + " to terminate", e); 106 | } 107 | 108 | if (mainProcess.isAlive()) { 109 | getLogger().info("Forcing " + getName() + " termination"); 110 | mainProcess.destroyForcibly(); 111 | } 112 | } 113 | }; 114 | forceDestroyThread.start(); 115 | } 116 | 117 | @Override 118 | public final boolean isRunning() { 119 | boolean result = mainThread != null && mainThread.isAlive(); 120 | if (!result) { 121 | forceDestroyThread = null; 122 | } 123 | return result; 124 | } 125 | 126 | @Override 127 | public final void setPort(int newPort) { 128 | this.port = newPort; 129 | } 130 | 131 | protected abstract String[] getLaunchCmd(); 132 | } 133 | -------------------------------------------------------------------------------- /src/core/ipc/IPCServiceManager.java: -------------------------------------------------------------------------------- 1 | package core.ipc; 2 | 3 | import java.io.IOException; 4 | import java.util.HashMap; 5 | import java.util.List; 6 | import java.util.Map; 7 | import java.util.logging.Logger; 8 | 9 | import utilities.Function; 10 | import argo.jdom.JsonNode; 11 | import argo.jdom.JsonNodeFactories; 12 | import core.ipc.repeatClient.CSharpIPCClientService; 13 | import core.ipc.repeatClient.PythonIPCClientService; 14 | import core.ipc.repeatServer.ControllerServer; 15 | import core.languageHandler.Language; 16 | 17 | public final class IPCServiceManager { 18 | 19 | private static final Logger LOGGER = Logger.getLogger(IPCServiceManager.class.getName()); 20 | 21 | public static final int IPC_SERVICE_COUNT = 3; 22 | private static final long INTER_SERVICE_BOOT_TIME_MS = 2000; 23 | private static final IIPCService[] ipcServices; 24 | private static final Map ipcByLanugage; 25 | 26 | static { 27 | ipcServices = new IIPCService[IPC_SERVICE_COUNT]; 28 | ipcServices[IPCServiceName.CONTROLLER_SERVER.value()] = new ControllerServer(); 29 | ipcServices[IPCServiceName.PYTHON.value()] = new PythonIPCClientService(); 30 | ipcServices[IPCServiceName.CSHARP.value()] = new CSharpIPCClientService(); 31 | 32 | ipcByLanugage = new HashMap<>(); 33 | ipcByLanugage.put(Language.JAVA, -1); 34 | ipcByLanugage.put(Language.PYTHON, IPCServiceName.PYTHON.value()); 35 | ipcByLanugage.put(Language.CSHARP, IPCServiceName.CSHARP.value()); 36 | } 37 | 38 | public static IIPCService getIPCService(Language name) { 39 | int index = ipcByLanugage.get(name); 40 | if (index >= 0) { 41 | return ipcServices[index]; 42 | } else { 43 | return null; 44 | } 45 | } 46 | 47 | public static IIPCService getIPCService(IPCServiceName name) { 48 | return ipcServices[name.value()]; 49 | } 50 | 51 | public static IIPCService getIPCService(int index) { 52 | if (index >= IPC_SERVICE_COUNT) { 53 | return null; 54 | } 55 | 56 | return ipcServices[index]; 57 | } 58 | 59 | public static void initiateServices() throws IOException { 60 | for (IPCServiceName name : IPCServiceName.ALL_SERVICE_NAMES) { 61 | IIPCService service = IPCServiceManager.getIPCService(name); 62 | if (!service.isLaunchAtStartup()) { 63 | continue; 64 | } 65 | service.startRunning(); 66 | LOGGER.info("Starting ipc service " + service.getName()); 67 | 68 | try { 69 | Thread.sleep(INTER_SERVICE_BOOT_TIME_MS); 70 | } catch (InterruptedException e) { 71 | } 72 | } 73 | } 74 | 75 | public static void stopServices() throws IOException { 76 | for (int i = IPCServiceName.ALL_SERVICE_NAMES.length - 1; i >= 0; i--) { 77 | IPCServiceName name = IPCServiceName.ALL_SERVICE_NAMES[i]; 78 | IIPCService service = IPCServiceManager.getIPCService(name); 79 | do { 80 | service.stopRunning(); 81 | try { 82 | Thread.sleep(100); 83 | } catch (InterruptedException e) { 84 | e.printStackTrace(); 85 | } 86 | } while (service.isRunning()); 87 | } 88 | } 89 | 90 | public static boolean parseJSON(List ipcSettings) { 91 | for (JsonNode language : ipcSettings) { 92 | String name = language.getStringValue("name"); 93 | boolean launchAtStartup = language.getBooleanValue("launch_at_startup"); 94 | Language currentLanguage = Language.identify(name); 95 | IIPCService service = IPCServiceManager.getIPCService(currentLanguage); 96 | if (service != null) { 97 | service.setLaunchAtStartup(launchAtStartup); 98 | } 99 | } 100 | return true; 101 | } 102 | 103 | public static JsonNode jsonize() { 104 | return JsonNodeFactories.array( 105 | new Function() { 106 | @Override 107 | public JsonNode apply(Language l) { 108 | IIPCService service = getIPCService(l); 109 | 110 | return JsonNodeFactories.object( 111 | JsonNodeFactories.field("name", JsonNodeFactories.string(l.toString())), 112 | JsonNodeFactories.field("launch_at_startup", JsonNodeFactories.booleanNode(service == null ? false : service.isLaunchAtStartup())) 113 | ); 114 | } 115 | }.map(Language.values()) 116 | ); 117 | } 118 | 119 | private IPCServiceManager() {} 120 | } 121 | -------------------------------------------------------------------------------- /src/core/ipc/repeatServer/processors/ServerMainProcessor.java: -------------------------------------------------------------------------------- 1 | package core.ipc.repeatServer.processors; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.logging.Level; 6 | import java.util.logging.Logger; 7 | 8 | import utilities.ILoggable; 9 | import utilities.JSONUtility; 10 | import argo.jdom.JsonNode; 11 | import argo.jdom.JsonRootNode; 12 | import core.controller.Core; 13 | import core.ipc.repeatServer.MainMessageSender; 14 | 15 | /** 16 | * This class represents the central message processor. 17 | * There are four types of messages received from the client: 18 | * 1) action: See {@link core.ipc.repeatServer.processors.ControllerRequestProcessor} 19 | * 2) task: See {@link core.ipc.repeatServer.processors.TaskProcessor} 20 | * 3) shared_memory: See {@link core.ipc.repeatServer.processors.SharedMemoryProcessor} 21 | * 4) system_host: See {@link core.ipc.repeatServer.processors.SystemRequestProcessor} 22 | * 5) system_client: See {@link core.ipc.repeatServer.processors.SystemRequestProcessor} 23 | * 24 | * A generic message received from client will have the following JSON format: 25 | * { 26 | * "type" : one of the four types above, 27 | * "id" : message id, 28 | * "content" : content to be processed by the upper layer 29 | * } 30 | * 31 | * Note that it is essential for a message sent with id X be replied with message of the same id from client. 32 | * Conversely, a message received from client with id X should also be replied with the same id to client. 33 | * 34 | * @author HP Truong 35 | * 36 | */ 37 | public class ServerMainProcessor implements ILoggable { 38 | 39 | private final Map messageProcesssors; 40 | private final ControllerRequestProcessor actionProcessor; 41 | private final TaskProcessor taskProcessor; 42 | private final SystemRequestProcessor systemProcessor; 43 | private final SharedMemoryProcessor sharedMemoryProcessor; 44 | 45 | public ServerMainProcessor(Core core, MainMessageSender messageSender) { 46 | messageProcesssors = new HashMap<>(); 47 | 48 | actionProcessor = new ControllerRequestProcessor(messageSender, core); 49 | taskProcessor = new TaskProcessor(messageSender); 50 | systemProcessor = new SystemRequestProcessor(messageSender, this); 51 | sharedMemoryProcessor = new SharedMemoryProcessor(messageSender); 52 | 53 | messageProcesssors.put(IpcMessageType.ACTION, actionProcessor); 54 | messageProcesssors.put(IpcMessageType.TASK, taskProcessor); 55 | messageProcesssors.put(IpcMessageType.SHARED_MEMORY, sharedMemoryProcessor); 56 | messageProcesssors.put(IpcMessageType.SYSTEM_HOST, systemProcessor); 57 | messageProcesssors.put(IpcMessageType.SYSTEM_CLIENT, systemProcessor); 58 | } 59 | 60 | /** 61 | * Parse a request from client. 62 | * @param message request from client as JSON string 63 | * @param core Core controller that will execute the action 64 | * @return list of actions need to perform in order 65 | */ 66 | public boolean processRequest(String message) { 67 | JsonRootNode root = JSONUtility.jsonFromString(message); 68 | if (root == null || !verifyMessage(root)) { 69 | getLogger().warning("Invalid messaged received " + message); 70 | return false; 71 | } 72 | 73 | getLogger().fine("Receive " + message); 74 | IpcMessageType type = IpcMessageType.identify(root.getStringValue("type")); 75 | long id = Long.parseLong(root.getNumberValue("id")); 76 | JsonNode content = root.getNode("content"); 77 | 78 | try { 79 | messageProcesssors.get(type).process(type.getValue(), id, content); 80 | return true; 81 | } catch (InterruptedException e) { 82 | getLogger().log(Level.WARNING, "Interrupted while processing message", e); 83 | return false; 84 | } 85 | } 86 | 87 | private boolean verifyMessage(JsonRootNode message) { 88 | return message.isStringValue("type") && 89 | message.isNumberValue("id") && 90 | message.isObjectNode("content") && 91 | messageProcesssors.containsKey(IpcMessageType.identify(message.getStringValue("type"))); 92 | } 93 | 94 | @Override 95 | public Logger getLogger() { 96 | return Logger.getLogger(ServerMainProcessor.class.getName()); 97 | } 98 | 99 | public TaskProcessor getTaskProcessor() { 100 | return taskProcessor; 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /src/utilities/ZipUtility.java: -------------------------------------------------------------------------------- 1 | package utilities; 2 | 3 | import java.io.File; 4 | import java.io.FileInputStream; 5 | import java.io.FileOutputStream; 6 | import java.io.IOException; 7 | import java.util.logging.Level; 8 | import java.util.logging.Logger; 9 | import java.util.zip.ZipEntry; 10 | import java.util.zip.ZipInputStream; 11 | import java.util.zip.ZipOutputStream; 12 | 13 | public class ZipUtility { 14 | 15 | private static final Logger LOGGER = Logger.getLogger(ZipUtility.class.getName()); 16 | 17 | /** 18 | * Zip a file 19 | * @param zipFile 20 | * @param outputFolder 21 | */ 22 | public static void unZipFile(String zipFile, String outputFolder) { 23 | byte[] buffer = new byte[1024]; 24 | 25 | try { 26 | // Create output directory is not exists 27 | File folder = new File(outputFolder); 28 | if (!folder.exists()) { 29 | folder.mkdir(); 30 | } 31 | 32 | // Get the zip file content 33 | ZipInputStream zipInputStream = new ZipInputStream(new FileInputStream(zipFile)); 34 | // Get the zipped file list entry 35 | ZipEntry zipEntry = zipInputStream.getNextEntry(); 36 | 37 | while (zipEntry != null) { 38 | String fileName = zipEntry.getName(); 39 | File newFile = new File(FileUtility.joinPath(outputFolder, fileName)); 40 | 41 | System.out.println("file unzip : " + newFile.getAbsoluteFile()); 42 | 43 | // create all non exists folders 44 | // else you will hit FileNotFoundException for compressed folder 45 | new File(newFile.getParent()).mkdirs(); 46 | 47 | FileOutputStream fileOutputStream = new FileOutputStream(newFile); 48 | 49 | int length; 50 | while ((length = zipInputStream.read(buffer)) > 0) { 51 | fileOutputStream.write(buffer, 0, length); 52 | } 53 | 54 | fileOutputStream.close(); 55 | zipEntry = zipInputStream.getNextEntry(); 56 | } 57 | 58 | zipInputStream.closeEntry(); 59 | zipInputStream.close(); 60 | } catch (IOException ex) { 61 | LOGGER.log(Level.WARNING, "Unable to unzip file", ex); 62 | } 63 | } 64 | 65 | /** 66 | * Zip a directory, output to a .zip file 67 | * @param zipDirectory directory to be zipped 68 | * @param output path to the output .zip file 69 | */ 70 | public static void zipDir(File zipDirectory, File output) { 71 | zipDir(zipDirectory.getAbsolutePath(), output.getAbsolutePath()); 72 | } 73 | 74 | /** 75 | * Zip a directory, output to a .zip file 76 | * @param zipDirectory directory to be zipped 77 | * @param outputFile path to the output .zip file 78 | */ 79 | public static void zipDir(String zipDirectory, String outputFile) { 80 | ZipOutputStream zip = null; 81 | FileOutputStream fileWriter = null; 82 | try { 83 | fileWriter = new FileOutputStream(outputFile); 84 | 85 | zip = new ZipOutputStream(fileWriter); 86 | addFolderToZip("", zipDirectory, zip); 87 | zip.close(); 88 | fileWriter.close(); 89 | } catch (IOException e) { 90 | LOGGER.log(Level.WARNING, "Unable to zip file", e); 91 | } 92 | } 93 | 94 | private static void addFolderToZip(String path, String srcFolder, ZipOutputStream zip) throws IOException { 95 | File folder = new File(srcFolder); 96 | if (folder.list().length == 0) { 97 | addFileToZip(path, srcFolder, zip, true); 98 | } else { 99 | for (String fileName : folder.list()) { 100 | if (path.equals("")) { 101 | addFileToZip(folder.getName(), srcFolder + "/" + fileName, zip, false); 102 | } else { 103 | addFileToZip(path + "/" + folder.getName(), srcFolder + "/" + fileName, zip, false); 104 | } 105 | } 106 | } 107 | } 108 | 109 | private static void addFileToZip(String path, String srcFile, ZipOutputStream zip, boolean flag) throws IOException { 110 | File folder = new File(srcFile); 111 | if (flag) { 112 | zip.putNextEntry(new ZipEntry(path + "/" + folder.getName() + "/")); 113 | } else { 114 | if (folder.isDirectory()) { 115 | addFolderToZip(path, srcFile, zip); 116 | } else { 117 | byte[] buf = new byte[1024]; 118 | int len; 119 | FileInputStream in = new FileInputStream(srcFile); 120 | zip.putNextEntry(new ZipEntry(path + "/" + folder.getName())); 121 | while ((len = in.read(buf)) > 0) { 122 | zip.write(buf, 0, len); 123 | } 124 | in.close(); 125 | } 126 | } 127 | } 128 | 129 | private ZipUtility() {} 130 | } -------------------------------------------------------------------------------- /src/core/userDefinedTask/SharedVariables.java: -------------------------------------------------------------------------------- 1 | package core.userDefinedTask; 2 | 3 | import java.util.HashMap; 4 | import java.util.Map; 5 | import java.util.logging.Logger; 6 | 7 | /** 8 | * Shared variables used to pass values between tasks. This only supports string values since the tasks can be 9 | * written in different programming languages. 10 | */ 11 | public class SharedVariables { 12 | 13 | private static final Logger LOGGER = Logger.getLogger(SharedVariables.class.getName()); 14 | 15 | public static final String GLOBAL_NAMESPACE = "global"; 16 | private static final Map> variables = new HashMap<>(); 17 | 18 | private final String namespace; 19 | 20 | public SharedVariables(String namespace) { 21 | this.namespace = namespace; 22 | } 23 | 24 | /** 25 | * Get a value of a variable for the current namespace. 26 | * 27 | * @param variable name of the variable. 28 | * @return value of the variable, or null if not exist. 29 | */ 30 | public String getVar(String variable) { 31 | return getVar(namespace, variable); 32 | } 33 | 34 | /** 35 | * Set the value for a variable in the current namespace. 36 | * 37 | * @param variable variable name. 38 | * @param value value of the variable. 39 | * @return the existing value of the variable, or null if the variable does not exist before. 40 | */ 41 | public String setVar(String variable, String value) { 42 | return setVar(namespace, variable, value); 43 | } 44 | 45 | /** 46 | * Delete the value for a variable in the current namespace. 47 | * 48 | * @param variable variable name. 49 | * @return the existing value of the variable, or null if the variable does not exist before. 50 | */ 51 | public String delVar(String variable) { 52 | return delVar(namespace, variable); 53 | } 54 | 55 | /** 56 | * Retrieve a variable value given namespace and variable name. 57 | * 58 | * @param namespace namespace where this variable belongs. 59 | * @param variable name of the variable. 60 | * @return value of the variable 61 | */ 62 | public static synchronized String getVar(String namespace, String variable) { 63 | Map namespaceVariables = variables.get(namespace); 64 | if (namespaceVariables == null) { 65 | return null; 66 | } 67 | 68 | return namespaceVariables.get(variable); 69 | } 70 | 71 | /** 72 | * Set the value for a variable in a namespace. 73 | * 74 | * @param namespace namespace where the variable belongs. 75 | * @param variable variable name. 76 | * @param value value of the variable. 77 | * @return the existing value of the variable, or null if the variable does not exist before. 78 | */ 79 | public static synchronized String setVar(String namespace, String variable, String value) { 80 | if (isNullValue(namespace, "namespace") || isNullValue(variable, "variable") || isNullValue(value, "value")) { 81 | return null; 82 | } 83 | 84 | if (!variables.containsKey(namespace)) { 85 | variables.put(namespace, new HashMap()); 86 | } 87 | 88 | Map namespaceVariables = variables.get(namespace); 89 | return namespaceVariables.put(variable, value); 90 | } 91 | 92 | /** 93 | * Delete the value for a variable in a namespace. 94 | * 95 | * @param namespace namespace where the variable belongs. 96 | * @param variable variable name. 97 | * @return the existing value of the variable, or null if the variable does not exist before. 98 | */ 99 | public static synchronized String delVar(String namespace, String variable) { 100 | if (isNullValue(namespace, "namespace") || isNullValue(variable, "variable")) { 101 | return null; 102 | } 103 | 104 | if (!variables.containsKey(namespace)) { 105 | return null; 106 | } 107 | 108 | Map namespaceVariables = variables.get(namespace); 109 | String result = namespaceVariables.remove(variable); 110 | 111 | if (namespaceVariables.isEmpty()) { 112 | variables.remove(namespace); 113 | } 114 | 115 | return result; 116 | } 117 | 118 | /** 119 | * Simply check if the value is null or not and log a warning message if it is null. 120 | * 121 | * @param value the value to check. 122 | * @param valueMeaning meaning of the value. 123 | * @return if the value is null. 124 | */ 125 | private static boolean isNullValue(String value, String valueMeaning) { 126 | if (value == null) { 127 | LOGGER.warning("Setting null " + valueMeaning + "!"); 128 | return true; 129 | } 130 | 131 | return false; 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /src/core/languageHandler/compiler/PythonRemoteCompiler.java: -------------------------------------------------------------------------------- 1 | package core.languageHandler.compiler; 2 | 3 | import java.io.File; 4 | import java.util.logging.Level; 5 | import java.util.logging.Logger; 6 | 7 | import javax.swing.JButton; 8 | import javax.swing.JFileChooser; 9 | import javax.swing.JFrame; 10 | import javax.swing.JOptionPane; 11 | 12 | import utilities.FileUtility; 13 | import utilities.Pair; 14 | import argo.jdom.JsonNode; 15 | import argo.jdom.JsonNodeFactories; 16 | import core.controller.Core; 17 | import core.ipc.IPCServiceManager; 18 | import core.ipc.IPCServiceName; 19 | import core.ipc.repeatClient.PythonIPCClientService; 20 | import core.languageHandler.Language; 21 | import core.userDefinedTask.UserDefinedAction; 22 | 23 | public class PythonRemoteCompiler extends AbstractRemoteNativeCompiler { 24 | 25 | private File interpreter; 26 | private final File objectFileDirectory; 27 | 28 | { 29 | getLogger().setLevel(Level.ALL); 30 | } 31 | 32 | public PythonRemoteCompiler(File objectFileDirectory) { 33 | interpreter = new File("python.exe"); 34 | this.objectFileDirectory = objectFileDirectory; 35 | } 36 | 37 | @Override 38 | public void setPath(File file) { 39 | interpreter = file; 40 | } 41 | 42 | @Override 43 | public File getPath() { 44 | return interpreter; 45 | } 46 | 47 | @Override 48 | protected Pair loadAction(final int id, final String source, final File sourceFile) { 49 | UserDefinedAction output = new UserDefinedAction() { 50 | @Override 51 | public void action(Core controller) { 52 | boolean result = remoteTaskManager.runTask(id, invoker); 53 | if (!result) { 54 | getLogger().warning("Unable to run task with id = " + id); 55 | } 56 | } 57 | 58 | @Override 59 | public UserDefinedAction recompile(AbstractNativeCompiler compiler, boolean clean) { 60 | Pair recompiled = PythonRemoteCompiler.this.compile(source); 61 | UserDefinedAction output = recompiled.getB(); 62 | output.syncContent(this); 63 | return output; 64 | } 65 | }; 66 | output.setSourcePath(sourceFile.getAbsolutePath()); 67 | 68 | getLogger().info("Successfully loaded action from remote compiler with id = " + id); 69 | return new Pair(DynamicCompilerOutput.COMPILATION_SUCCESS, output); 70 | } 71 | 72 | @Override 73 | protected File getSourceFile(String compilingAction) { 74 | return new File(FileUtility.joinPath(objectFileDirectory.getAbsolutePath(), compilingAction + this.getExtension())); 75 | } 76 | 77 | @Override 78 | public Language getName() { 79 | return Language.PYTHON; 80 | } 81 | 82 | @Override 83 | public String getExtension() { 84 | return ".py"; 85 | } 86 | 87 | @Override 88 | public String getObjectExtension() { 89 | return ".py"; 90 | } 91 | 92 | @Override 93 | public boolean parseCompilerSpecificArgs(JsonNode node) { 94 | return true; 95 | } 96 | 97 | @Override 98 | public JsonNode getCompilerSpecificArgs() { 99 | return JsonNodeFactories.object(); 100 | } 101 | 102 | @Override 103 | protected String getDummyPrefix() { 104 | return "PY_"; 105 | } 106 | 107 | @Override 108 | public Logger getLogger() { 109 | return Logger.getLogger(PythonRemoteCompiler.class.getName()); 110 | } 111 | 112 | @Override 113 | protected boolean checkRemoteCompilerSettings() { 114 | if (!FileUtility.fileExists(interpreter) || !interpreter.canExecute()) { 115 | getLogger().severe("No interpreter found at " + interpreter.getAbsolutePath()); 116 | return false; 117 | } 118 | 119 | return true; 120 | } 121 | 122 | /*******************************************************************/ 123 | /************************Swing components***************************/ 124 | /*******************************************************************/ 125 | 126 | @Override 127 | public void promptChangePath(JFrame parent) { 128 | JFileChooser chooser = new JFileChooser(getPath()); 129 | chooser.setFileSelectionMode(JFileChooser.FILES_ONLY); 130 | if (chooser.showDialog(parent, "Set Python interpreter") == JFileChooser.APPROVE_OPTION) { 131 | File selectedFile = chooser.getSelectedFile(); 132 | if (!selectedFile.canExecute()) { 133 | JOptionPane.showMessageDialog(parent, 134 | "Chosen file " + selectedFile.getName() + " is not executable", "Invalid choice", JOptionPane.WARNING_MESSAGE); 135 | return; 136 | } 137 | setPath(selectedFile); 138 | ((PythonIPCClientService)IPCServiceManager.getIPCService(IPCServiceName.PYTHON)).setExecutingProgram(selectedFile); 139 | } 140 | } 141 | 142 | @Override 143 | public void changeCompilationButton(JButton bCompile) { 144 | bCompile.setText("Load source"); 145 | File interpreter = getPath(); 146 | getLogger().info("Using python interpreter at " + interpreter.getAbsolutePath()); 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /src/staticResources/BootStrapResources.java: -------------------------------------------------------------------------------- 1 | package staticResources; 2 | 3 | import java.awt.Image; 4 | import java.io.IOException; 5 | import java.io.InputStream; 6 | import java.util.HashMap; 7 | import java.util.Map; 8 | import java.util.logging.Level; 9 | import java.util.logging.Logger; 10 | 11 | import javax.imageio.ImageIO; 12 | import javax.swing.ImageIcon; 13 | 14 | import utilities.FileUtility; 15 | import core.config.Config; 16 | import core.languageHandler.Language; 17 | 18 | public class BootStrapResources { 19 | 20 | private static final Logger LOGGER = Logger.getLogger(BootStrapResources.class.getName()); 21 | 22 | private static final Map LANGUAGE_API; 23 | private static final Map NATIVE_LANGUAGE_TEMPLATES; 24 | private static final Map NATIVE_BOOTSTRAP_RESOURCES; 25 | 26 | public static final Image TRAY_IMAGE; 27 | public static final ImageIcon UP, DOWN, DELETE, ADD, EDIT, MOVE, REFRESH; 28 | public static final ImageIcon RECORD, STOP, PLAY, SELECT; 29 | 30 | static { 31 | TRAY_IMAGE = getImage("/staticResources/Repeat.jpg"); 32 | 33 | ADD = getIcon("/toolbarButtonGraphics/general/Add24.gif"); 34 | EDIT = getIcon("/toolbarButtonGraphics/general/Edit24.gif"); 35 | DELETE = getIcon("/toolbarButtonGraphics/general/Delete24.gif"); 36 | 37 | MOVE = getIcon("/toolbarButtonGraphics/general/Redo24.gif"); 38 | REFRESH = getIcon("/toolbarButtonGraphics/general/Refresh24.gif"); 39 | 40 | UP = getIcon("/toolbarButtonGraphics/navigation/Up24.gif"); 41 | DOWN = getIcon("/toolbarButtonGraphics/navigation/Down24.gif"); 42 | 43 | RECORD = getIcon("/toolbarButtonGraphics/general/Stop16.gif"); 44 | STOP = getIcon("/toolbarButtonGraphics/media/Stop16.gif"); 45 | 46 | PLAY = getIcon("/toolbarButtonGraphics/media/Play16.gif"); 47 | 48 | SELECT = getIcon("/toolbarButtonGraphics/general/Preferences24.gif"); 49 | 50 | /*********************************************************************************/ 51 | LANGUAGE_API = new HashMap<>(); 52 | LANGUAGE_API.put(Language.JAVA, getFile("/core/languageHandler/API/JavaAPI.txt")); 53 | LANGUAGE_API.put(Language.PYTHON, getFile("/core/languageHandler/API/PythonAPI.txt")); 54 | LANGUAGE_API.put(Language.CSHARP, getFile("/core/languageHandler/API/CSharpAPI.txt")); 55 | 56 | /*********************************************************************************/ 57 | NATIVE_LANGUAGE_TEMPLATES = new HashMap<>(); 58 | NATIVE_LANGUAGE_TEMPLATES.put(Language.JAVA, getFile("/natives/java/TemplateRepeat")); 59 | NATIVE_LANGUAGE_TEMPLATES.put(Language.PYTHON, getFile("/natives/python/template_repeat.py")); 60 | NATIVE_LANGUAGE_TEMPLATES.put(Language.CSHARP, getFile("/natives/csharp/source/TemplateRepeat.cs")); 61 | 62 | /*********************************************************************************/ 63 | NATIVE_BOOTSTRAP_RESOURCES = new HashMap<>(); 64 | NATIVE_BOOTSTRAP_RESOURCES.put(Language.PYTHON, new PythonResources()); 65 | NATIVE_BOOTSTRAP_RESOURCES.put(Language.CSHARP, new CSharpResources()); 66 | } 67 | 68 | public static AbstractNativeBootstrapResource getBootstrapResource(Language language) { 69 | return NATIVE_BOOTSTRAP_RESOURCES.get(language); 70 | } 71 | 72 | public static void extractResources() throws IOException { 73 | for (Language language : Language.values()) { 74 | AbstractNativeBootstrapResource resource = NATIVE_BOOTSTRAP_RESOURCES.get(language); 75 | if (resource != null) { 76 | resource.extractResources(); 77 | } 78 | } 79 | } 80 | 81 | protected static ImageIcon getIcon(String resource) { 82 | return new ImageIcon(getImage(resource)); 83 | } 84 | 85 | protected static Image getImage(String resource) { 86 | try { 87 | return ImageIO.read(BootStrapResources.class.getResourceAsStream(resource)); 88 | } catch (IOException e) { 89 | LOGGER.log(Level.SEVERE, "Cannot load image " + resource, e); 90 | return null; 91 | } 92 | } 93 | 94 | protected static String getFile(String path) { 95 | return FileUtility.readFromStream(BootStrapResources.class.getResourceAsStream(path)).toString(); 96 | } 97 | 98 | public static InputStream test() { 99 | return BootStrapResources.class.getResourceAsStream("/python"); 100 | } 101 | 102 | public static String getAbout() { 103 | return "Repeat " + Config.RELEASE_VERSION + "\n" 104 | + "A tool to repeat yourself with some intelligence.\n" 105 | + "Created by HP Truong. Contact me at hptruong93@gmail.com."; 106 | } 107 | 108 | public static String getAPI(Language language) { 109 | if (LANGUAGE_API.containsKey(language)) { 110 | return LANGUAGE_API.get(language); 111 | } else { 112 | return ""; 113 | } 114 | } 115 | 116 | public static String getNativeLanguageTemplate(Language language) { 117 | if (NATIVE_LANGUAGE_TEMPLATES.containsKey(language)) { 118 | return NATIVE_LANGUAGE_TEMPLATES.get(language); 119 | } else { 120 | return ""; 121 | } 122 | } 123 | 124 | private BootStrapResources() {} 125 | } 126 | -------------------------------------------------------------------------------- /src/frontEnd/IpcFrame.java: -------------------------------------------------------------------------------- 1 | package frontEnd; 2 | 3 | import java.awt.event.ActionEvent; 4 | import java.awt.event.ActionListener; 5 | import java.awt.event.MouseAdapter; 6 | import java.awt.event.MouseEvent; 7 | import java.awt.event.WindowEvent; 8 | import java.awt.event.WindowFocusListener; 9 | 10 | import javax.swing.GroupLayout; 11 | import javax.swing.GroupLayout.Alignment; 12 | import javax.swing.JButton; 13 | import javax.swing.JFrame; 14 | import javax.swing.JPanel; 15 | import javax.swing.JScrollPane; 16 | import javax.swing.JTable; 17 | import javax.swing.LayoutStyle.ComponentPlacement; 18 | import javax.swing.SwingConstants; 19 | import javax.swing.border.EmptyBorder; 20 | import javax.swing.table.DefaultTableCellRenderer; 21 | import javax.swing.table.DefaultTableModel; 22 | 23 | import staticResources.BootStrapResources; 24 | 25 | @SuppressWarnings("serial") 26 | public class IpcFrame extends JFrame { 27 | 28 | protected static final int COLUMN_NAME = 0; 29 | protected static final int COLUMN_PORT = 1; 30 | protected static final int COLUMN_STATUS = 2; 31 | protected static final int COLUMN_LAUCNH_AT_STARTUP = 3; 32 | 33 | private final JPanel contentPane; 34 | protected final JTable tIpc; 35 | protected final MainBackEndHolder mainFrame; 36 | 37 | protected IpcBackendHolder ipcBackEndHolder; 38 | 39 | /** 40 | * Create the frame. 41 | */ 42 | public IpcFrame(final MainBackEndHolder mainFrame) { 43 | this.mainFrame = mainFrame; 44 | ipcBackEndHolder = new IpcBackendHolder(this); 45 | addWindowFocusListener(new WindowFocusListener() { 46 | @Override 47 | public void windowGainedFocus(WindowEvent e) { 48 | ipcBackEndHolder.periodicRefresh(); 49 | } 50 | 51 | @Override 52 | public void windowLostFocus(WindowEvent e) { 53 | ipcBackEndHolder.stopPeriodicRefresh(); 54 | } 55 | }); 56 | 57 | setTitle("IPC Modules"); 58 | setDefaultCloseOperation(JFrame.HIDE_ON_CLOSE); 59 | setBounds(100, 100, 450, 300); 60 | contentPane = new JPanel(); 61 | contentPane.setBorder(new EmptyBorder(5, 5, 5, 5)); 62 | setContentPane(contentPane); 63 | 64 | JButton bStart = new JButton(); 65 | bStart.addActionListener(new ActionListener() { 66 | @Override 67 | public void actionPerformed(ActionEvent e) { 68 | ipcBackEndHolder.startProcess(); 69 | } 70 | }); 71 | bStart.setIcon(BootStrapResources.PLAY); 72 | bStart.setToolTipText("Start this ipc service"); 73 | 74 | JButton bStop = new JButton(); 75 | bStop.addActionListener(new ActionListener() { 76 | @Override 77 | public void actionPerformed(ActionEvent e) { 78 | ipcBackEndHolder.stopProcess(); 79 | } 80 | }); 81 | bStop.setIcon(BootStrapResources.STOP); 82 | bStop.setToolTipText("Stop this ipc service"); 83 | 84 | JScrollPane scrollPane = new JScrollPane(); 85 | GroupLayout gl_contentPane = new GroupLayout(contentPane); 86 | gl_contentPane.setHorizontalGroup( 87 | gl_contentPane.createParallelGroup(Alignment.LEADING) 88 | .addGroup(gl_contentPane.createSequentialGroup() 89 | .addContainerGap() 90 | .addGroup(gl_contentPane.createParallelGroup(Alignment.LEADING) 91 | .addComponent(scrollPane, GroupLayout.PREFERRED_SIZE, 412, GroupLayout.PREFERRED_SIZE) 92 | .addGroup(gl_contentPane.createSequentialGroup() 93 | .addComponent(bStart) 94 | .addPreferredGap(ComponentPlacement.RELATED) 95 | .addComponent(bStop))) 96 | .addContainerGap(GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)) 97 | ); 98 | gl_contentPane.setVerticalGroup( 99 | gl_contentPane.createParallelGroup(Alignment.LEADING) 100 | .addGroup(gl_contentPane.createSequentialGroup() 101 | .addContainerGap() 102 | .addGroup(gl_contentPane.createParallelGroup(Alignment.BASELINE) 103 | .addComponent(bStart, GroupLayout.PREFERRED_SIZE, 33, GroupLayout.PREFERRED_SIZE) 104 | .addComponent(bStop, GroupLayout.PREFERRED_SIZE, 33, GroupLayout.PREFERRED_SIZE)) 105 | .addPreferredGap(ComponentPlacement.RELATED) 106 | .addComponent(scrollPane, GroupLayout.DEFAULT_SIZE, 201, Short.MAX_VALUE) 107 | .addContainerGap()) 108 | ); 109 | 110 | tIpc = new JTable(); 111 | tIpc.addMouseListener(new MouseAdapter() { 112 | @Override 113 | public void mouseReleased(MouseEvent e) { 114 | ipcBackEndHolder.mouseReleasedIPCTable(e); 115 | } 116 | }); 117 | tIpc.setModel(new DefaultTableModel( 118 | new Object[][] { 119 | {null, null, null, null}, 120 | }, 121 | new String[] { 122 | "Process", "Port", "Running", "Launch at startup" 123 | } 124 | )); 125 | tIpc.getColumnModel().getColumn(0).setPreferredWidth(103); 126 | tIpc.getColumnModel().getColumn(3).setPreferredWidth(115); 127 | 128 | DefaultTableCellRenderer centerRender = new DefaultTableCellRenderer(); 129 | centerRender.setHorizontalAlignment(SwingConstants.CENTER); 130 | for (int i = 0 ; i < tIpc.getColumnCount(); i++) { 131 | tIpc.getColumnModel().getColumn(i).setCellRenderer(centerRender); 132 | } 133 | ipcBackEndHolder.renderServices(); 134 | scrollPane.setViewportView(tIpc); 135 | contentPane.setLayout(gl_contentPane); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/frontEnd/Main.java: -------------------------------------------------------------------------------- 1 | package frontEnd; 2 | 3 | import java.awt.EventQueue; 4 | import java.io.FileNotFoundException; 5 | import java.io.IOException; 6 | import java.io.PrintStream; 7 | import java.util.logging.ConsoleHandler; 8 | import java.util.logging.Level; 9 | import java.util.logging.Logger; 10 | 11 | import javax.swing.JOptionPane; 12 | import javax.swing.SwingWorker; 13 | import javax.swing.UIManager; 14 | import javax.swing.UIManager.LookAndFeelInfo; 15 | 16 | import org.jnativehook.GlobalScreen; 17 | import org.jnativehook.NativeHookException; 18 | 19 | import staticResources.BootStrapResources; 20 | import utilities.Function; 21 | import utilities.logging.ExceptionUtility; 22 | import utilities.logging.OutStream; 23 | import utilities.swing.KeyChainInputPanel; 24 | 25 | public class Main { 26 | 27 | private static final Logger LOGGER = Logger.getLogger(Main.class.getName()); 28 | private static MainFrame createdFrame; 29 | 30 | public static void main(String[] args) throws FileNotFoundException { 31 | /*************************************************************************************/ 32 | // Get the logger for "org.jnativehook" and set the level to WARNING to begin with. 33 | Logger logger = Logger.getLogger(GlobalScreen.class.getPackage().getName()); 34 | logger.setLevel(Level.WARNING); 35 | 36 | if (!GlobalScreen.isNativeHookRegistered()) { 37 | try { 38 | GlobalScreen.registerNativeHook(); 39 | } catch (NativeHookException e) { 40 | LOGGER.severe("Cannot register native hook!"); 41 | System.exit(1); 42 | } 43 | } 44 | /*************************************************************************************/ 45 | /********************************Extracting resources*********************************/ 46 | try { 47 | BootStrapResources.extractResources(); 48 | } catch (IOException e) { 49 | LOGGER.log(Level.SEVERE, "Cannot extract bootstrap resources.", e); 50 | System.exit(1); 51 | } 52 | 53 | /*************************************************************************************/ 54 | /********************************Defining backend activities**************************/ 55 | final SwingWorker backEndInitialization = new SwingWorker() { 56 | @Override 57 | protected Void doInBackground() throws Exception { 58 | if (createdFrame == null) { 59 | LOGGER.severe("Main frame is not created. Exitting..."); 60 | System.exit(2); 61 | } 62 | 63 | createdFrame.backEnd.loadConfig(null); 64 | 65 | try { 66 | createdFrame.backEnd.keysManager.startGlobalListener(); 67 | } catch (NativeHookException e) { 68 | e.printStackTrace(); 69 | } 70 | createdFrame.backEnd.keysManager.setDisablingFunction(new Function(){ 71 | @Override 72 | public Boolean apply(Void r) { 73 | return createdFrame.hotkey.isVisible() || KeyChainInputPanel.isInUse(); 74 | } 75 | }); 76 | 77 | createdFrame.backEnd.configureMainHotkeys(); 78 | /*************************************************************************************/ 79 | createdFrame.backEnd.renderTaskGroup(); 80 | createdFrame.backEnd.renderTasks(); 81 | 82 | PrintStream printStream = new PrintStream(new OutStream(createdFrame.taStatus)); 83 | System.setOut(printStream); 84 | System.setErr(printStream); 85 | Logger.getLogger("").addHandler(new ConsoleHandler()); 86 | /*************************************************************************************/ 87 | 88 | createdFrame.backEnd.initiateBackEndActivities(); 89 | return null; 90 | } 91 | }; 92 | 93 | /*************************************************************************************/ 94 | /********************************Start main program***********************************/ 95 | try { 96 | for (LookAndFeelInfo info : UIManager.getInstalledLookAndFeels()) { 97 | if ("Nimbus".equals(info.getName())) { 98 | UIManager.setLookAndFeel(info.getClassName()); 99 | break; 100 | } 101 | } 102 | } catch (Exception e) { 103 | // If Nimbus is not available, you can set the GUI to another look and feel. 104 | try { 105 | UIManager.setLookAndFeel(UIManager.getSystemLookAndFeelClassName()); 106 | } catch (Exception e1) { 107 | e.printStackTrace(); 108 | // We can still be functional. Just need to notify the user about the problem 109 | } 110 | } 111 | 112 | EventQueue.invokeLater(new Runnable() { 113 | @Override 114 | public void run() { 115 | MainFrame frame = null; 116 | 117 | try { 118 | frame = new MainFrame(); 119 | LOGGER.info("Successfully intialized application"); 120 | frame.setVisible(true); 121 | } catch (Exception e) { 122 | e.printStackTrace(); 123 | JOptionPane.showMessageDialog(null, ExceptionUtility.getStackTrace(e)); 124 | System.exit(2); 125 | } 126 | 127 | if (frame == null) { 128 | LOGGER.info("Failed to initialize GUI."); 129 | System.exit(3); 130 | } 131 | 132 | createdFrame = frame; 133 | backEndInitialization.execute(); 134 | } 135 | }); 136 | } 137 | } 138 | --------------------------------------------------------------------------------