Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- package net.acomputerdog.gotojava;
- import java.util.*;
- /**
- * Unlocks Java 8's secret GOTO feature.
- * To use, just add "import static net.acomputerdog.gotojava.Goto.*;" to the top of all classes.
- *
- * Version: 1.0.0.
- */
- public class Goto {
- /**
- * Map of Blocks to instances. Used to keep track of execution and local variables.
- */
- private static Map<Block, GotoInstance> objectMap = new HashMap<>();
- /**
- * Shared object to speed up loading lines of code
- */
- private static Block currentBObject = null;
- /**
- * Block instance used to retrieve items from objectMap without allocating new Blocks.
- * DO NOT USE TO STORE! THIS IS ONLY FOR GETTING!
- */
- private static Block blockGetter = new Block(null, 0);
- /**
- * An instance of a Goto block, used to keep track of execution and local variables
- */
- private static class GotoInstance {
- /**
- * The block that this GotoInstance keeps track of
- */
- private final Block block;
- /**
- * A map of line labels to line numbers
- */
- private final Map<String, Integer> lineMap = new HashMap<>();
- /**
- * List containing all lines in this block.
- */
- private final List<Line> lines = new ArrayList<>();
- /**
- * Local variables for this block
- */
- private final Map<String, Object> variables = new HashMap<>();
- /**
- * The next unused line number
- */
- private int nextFreeLineNumber;
- /**
- * The line currently being executed.
- * If updated by outside code (like GoTo) then currentLineChanged must be set to true to lock the value
- */
- private Line currentLine;
- /**
- * If true, currentLine will not be automatically moved to next line of code
- */
- private boolean currentLineChanged = false;
- private GotoInstance(Block block) {
- this.block = block;
- if (block == null) {
- throw new NullObjectException();
- }
- }
- /**
- * Adds a line of code
- * @param label Label for the line. Can be null.
- * @param code The code to run
- */
- private void addLine(String label, Runnable code) {
- if (code == null) {
- throw new NullCodeException();
- }
- int lineNum = getNextFreeLineNumber();
- if (label != null) {
- lineMap.put(label, lineNum);
- }
- lines.add(new Line(this, lineNum, label, code));
- }
- private int getNextFreeLineNumber() {
- int next = nextFreeLineNumber;
- nextFreeLineNumber++;
- return next;
- }
- /**
- * Runs the block of code, starting from the first line.
- */
- private void run() {
- if (lines.size() > 0) { //don't run an empty block
- currentLine = lines.get(0); //get first line
- while (currentLine != null) { //keep looping until the end of the block is reached
- currentLineChanged = false; //reset changed flag so that it can be set again
- currentLine.runnable.run(); //run the current line of code
- if (!currentLineChanged) { //if the code did not change the next line, then move it here
- int nextIndex = currentLine.line + 1; //get next index
- if (nextIndex < lines.size()) { //make sure it is in bounds
- currentLine = lines.get(nextIndex); //load the next line
- } else {
- currentLine = null; //end loop
- }
- }
- }
- }
- }
- private void GoTo(int line) {
- if (line < 0 || line >= lines.size()) {
- throw new IndexOutOfBoundsException();
- }
- currentLine = lines.get(line); //load next line
- currentLineChanged = true; //set changed flag
- }
- }
- /**
- * Gets the GotoInstance controlled by the specified Object and block ID
- * @param object The object. Can't be null
- * @param id The block ID
- */
- private static GotoInstance getInstance(Object object, int id) {
- if (object == null) {
- throw new NullObjectException();
- }
- blockGetter.obj = object;
- blockGetter.id = id;
- return objectMap.get(blockGetter);
- }
- /**
- * Go to a line of code. Must be in same block, despite appearances
- * @param obj The object to jump to
- * @param id The block id to jump to
- * @param line The line to jump to
- */
- public static void GoTo(Object obj, int id, int line) {
- if (obj == null) {
- throw new NullObjectException();
- }
- GotoInstance instance = getInstance(obj, id);
- instance.GoTo(line);
- }
- /**
- * Go to a line of code. Must be in same block, despite appearances
- * @param obj The object to jump to
- * @param id The block id to jump to
- * @param label The label to jump to
- */
- public static void GoTo(Object obj, int id, String label) {
- if (obj == null) {
- throw new NullObjectException();
- }
- if (label == null) {
- throw new NullLabelException();
- }
- GotoInstance instance = getInstance(obj, id);
- int line = instance.lineMap.get(label);
- instance.GoTo(line);
- }
- /**
- * Gets the value of a variable
- */
- public static <T> T val(Object obj, int id, String name) {
- if (obj == null) {
- throw new NullObjectException();
- }
- GotoInstance instance = getInstance(obj, id);
- try {
- return (T) instance.variables.get(name);
- } catch (ClassCastException e) {
- return null;
- }
- }
- /**
- * Sets the value of the variable
- */
- public static <T> void var(Object obj, int id, String name, T value) {
- if (obj == null) {
- throw new NullObjectException();
- }
- GotoInstance instance = getInstance(obj, id);
- instance.variables.put(name, value);
- }
- /**
- * Adds a line of code
- */
- public static void l(String label, Runnable code) {
- GotoInstance instance = objectMap.get(currentBObject);
- instance.addLine(label, code);
- }
- /**
- * Adds a line of code
- */
- public static void l(Runnable code) {
- l(null, code);
- }
- /**
- * Adds a new Block
- */
- public static void b(Object obj, int id, Runnable code) {
- if (obj == null) {
- throw new NullObjectException();
- }
- if (code == null) {
- throw new NullCodeException();
- }
- Block block = new Block(obj, id);
- currentBObject = block;
- GotoInstance instance = new GotoInstance(block);
- objectMap.put(block, instance);
- code.run(); //add lines
- instance.run(); //run lines
- objectMap.put(block, null);
- }
- /**
- * A line of code
- */
- private static class Line implements Comparable<Line> {
- private final GotoInstance instance;
- private final int line;
- private final String label;
- private final Runnable runnable;
- private Line(GotoInstance instance, int line, String label, Runnable runnable) {
- this.instance = instance;
- this.line = line;
- this.label = label;
- this.runnable = runnable;
- }
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof Line)) return false;
- Line line1 = (Line) o;
- if (line != line1.line) return false;
- if (instance != line1.instance) return false; //intentionally not using .equals
- return true;
- }
- @Override
- public int hashCode() {
- int result = instance.hashCode();
- result = 31 * result + line;
- return result;
- }
- @Override
- public int compareTo(Line o) {
- return line - o.line;
- }
- }
- private static class Block {
- private Object obj;
- private int id;
- private Block(Object obj, int id) {
- this.obj = obj;
- this.id = id;
- }
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (!(o instanceof Block)) return false;
- Block block = (Block) o;
- if (id != block.id) return false;
- return obj == block.obj; //intentionally not using .equals
- }
- @Override
- public int hashCode() {
- int result = obj.hashCode();
- result = 31 * result + id;
- return result;
- }
- }
- /**
- * Thrown when an object reference is null
- */
- private static class NullObjectException extends IllegalArgumentException {
- }
- /**
- * Thrown when a code reference is null
- */
- private static class NullCodeException extends IllegalArgumentException {
- }
- /**
- * Thrown when a label reference is null
- */
- private static class NullLabelException extends IllegalArgumentException {
- }
- }
Add Comment
Please, Sign In to add comment