acomputerdog

Java 8 GOTO unlocker

Feb 9th, 2016
669
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Java 9.56 KB | None | 0 0
  1. package net.acomputerdog.gotojava;
  2.  
  3. import java.util.*;
  4.  
  5. /**
  6.  * Unlocks Java 8's secret GOTO feature.
  7.  * To use, just add "import static net.acomputerdog.gotojava.Goto.*;" to the top of all classes.
  8.  *
  9.  * Version: 1.0.0.
  10.  */
  11. public class Goto {
  12.     /**
  13.      * Map of Blocks to instances.  Used to keep track of execution and local variables.
  14.      */
  15.     private static Map<Block, GotoInstance> objectMap = new HashMap<>();
  16.  
  17.     /**
  18.      * Shared object to speed up loading lines of code
  19.      */
  20.     private static Block currentBObject = null;
  21.  
  22.     /**
  23.      * Block instance used to retrieve items from objectMap without allocating new Blocks.
  24.      * DO NOT USE TO STORE!  THIS IS ONLY FOR GETTING!
  25.      */
  26.     private static Block blockGetter = new Block(null, 0);
  27.  
  28.     /**
  29.      * An instance of a Goto block, used to keep track of execution and local variables
  30.      */
  31.     private static class GotoInstance {
  32.         /**
  33.          * The block that this GotoInstance keeps track of
  34.          */
  35.         private final Block block;
  36.  
  37.         /**
  38.          * A map of line labels to line numbers
  39.          */
  40.         private final Map<String, Integer> lineMap = new HashMap<>();
  41.  
  42.         /**
  43.          * List containing all lines in this block.
  44.          */
  45.         private final List<Line> lines = new ArrayList<>();
  46.  
  47.         /**
  48.          * Local variables for this block
  49.          */
  50.         private final Map<String, Object> variables = new HashMap<>();
  51.  
  52.         /**
  53.          * The next unused line number
  54.          */
  55.         private int nextFreeLineNumber;
  56.  
  57.         /**
  58.          * The line currently being executed.
  59.          * If updated by outside code (like GoTo) then currentLineChanged must be set to true to lock the value
  60.          */
  61.         private Line currentLine;
  62.  
  63.         /**
  64.          * If true, currentLine will not be automatically moved to next line of code
  65.          */
  66.         private boolean currentLineChanged = false;
  67.  
  68.         private GotoInstance(Block block) {
  69.             this.block = block;
  70.             if (block == null) {
  71.                 throw new NullObjectException();
  72.             }
  73.         }
  74.  
  75.         /**
  76.          * Adds a line of code
  77.          * @param label Label for the line.  Can be null.
  78.          * @param code The code to run
  79.          */
  80.         private void addLine(String label, Runnable code) {
  81.             if (code == null) {
  82.                 throw new NullCodeException();
  83.             }
  84.             int lineNum = getNextFreeLineNumber();
  85.             if (label != null) {
  86.                 lineMap.put(label, lineNum);
  87.             }
  88.             lines.add(new Line(this, lineNum, label, code));
  89.         }
  90.  
  91.         private int getNextFreeLineNumber() {
  92.             int next = nextFreeLineNumber;
  93.             nextFreeLineNumber++;
  94.             return next;
  95.         }
  96.  
  97.         /**
  98.          * Runs the block of code, starting from the first line.
  99.          */
  100.         private void run() {
  101.             if (lines.size() > 0) { //don't run an empty block
  102.                 currentLine = lines.get(0); //get first line
  103.                 while (currentLine != null) { //keep looping until the end of the block is reached
  104.                     currentLineChanged = false; //reset changed flag so that it can be set again
  105.                     currentLine.runnable.run(); //run the current line of code
  106.                     if (!currentLineChanged) { //if the code did not change the next line, then move it here
  107.                         int nextIndex = currentLine.line + 1; //get next index
  108.                         if (nextIndex < lines.size()) { //make sure it is in bounds
  109.                             currentLine = lines.get(nextIndex); //load the next line
  110.                         } else {
  111.                             currentLine = null; //end loop
  112.                         }
  113.                     }
  114.                 }
  115.             }
  116.         }
  117.  
  118.         private void GoTo(int line) {
  119.             if (line < 0 || line >= lines.size()) {
  120.                 throw new IndexOutOfBoundsException();
  121.             }
  122.             currentLine = lines.get(line); //load next line
  123.             currentLineChanged = true; //set changed flag
  124.         }
  125.     }
  126.  
  127.     /**
  128.      * Gets the GotoInstance controlled by the specified Object and block ID
  129.      * @param object The object.  Can't be null
  130.      * @param id The block ID
  131.      */
  132.     private static GotoInstance getInstance(Object object, int id) {
  133.         if (object == null) {
  134.             throw new NullObjectException();
  135.         }
  136.         blockGetter.obj = object;
  137.         blockGetter.id = id;
  138.         return objectMap.get(blockGetter);
  139.     }
  140.  
  141.     /**
  142.      * Go to a line of code.  Must be in same block, despite appearances
  143.      * @param obj The object to jump to
  144.      * @param id The block id to jump to
  145.      * @param line The line to jump to
  146.      */
  147.     public static void GoTo(Object obj, int id, int line) {
  148.         if (obj == null) {
  149.             throw new NullObjectException();
  150.         }
  151.         GotoInstance instance = getInstance(obj, id);
  152.         instance.GoTo(line);
  153.     }
  154.  
  155.     /**
  156.      * Go to a line of code.  Must be in same block, despite appearances
  157.      * @param obj The object to jump to
  158.      * @param id The block id to jump to
  159.      * @param label The label to jump to
  160.      */
  161.     public static void GoTo(Object obj, int id, String label) {
  162.         if (obj == null) {
  163.             throw new NullObjectException();
  164.         }
  165.         if (label == null) {
  166.             throw new NullLabelException();
  167.         }
  168.         GotoInstance instance = getInstance(obj, id);
  169.         int line = instance.lineMap.get(label);
  170.         instance.GoTo(line);
  171.     }
  172.  
  173.     /**
  174.      * Gets the value of a variable
  175.      */
  176.     public static <T> T val(Object obj, int id, String name) {
  177.         if (obj == null) {
  178.             throw new NullObjectException();
  179.         }
  180.         GotoInstance instance = getInstance(obj, id);
  181.         try {
  182.             return (T) instance.variables.get(name);
  183.         } catch (ClassCastException e) {
  184.             return null;
  185.         }
  186.     }
  187.  
  188.     /**
  189.      * Sets the value of the variable
  190.      */
  191.     public static <T> void var(Object obj, int id, String name, T value) {
  192.         if (obj == null) {
  193.             throw new NullObjectException();
  194.         }
  195.         GotoInstance instance = getInstance(obj, id);
  196.         instance.variables.put(name, value);
  197.     }
  198.  
  199.     /**
  200.      * Adds a line of code
  201.      */
  202.     public static void l(String label, Runnable code) {
  203.         GotoInstance instance = objectMap.get(currentBObject);
  204.         instance.addLine(label, code);
  205.     }
  206.  
  207.     /**
  208.      * Adds a line of code
  209.      */
  210.     public static void l(Runnable code) {
  211.         l(null, code);
  212.     }
  213.  
  214.     /**
  215.      * Adds a new Block
  216.      */
  217.     public static void b(Object obj, int id, Runnable code) {
  218.         if (obj == null) {
  219.             throw new NullObjectException();
  220.         }
  221.         if (code == null) {
  222.             throw new NullCodeException();
  223.         }
  224.         Block block = new Block(obj, id);
  225.         currentBObject = block;
  226.         GotoInstance instance = new GotoInstance(block);
  227.         objectMap.put(block, instance);
  228.         code.run(); //add lines
  229.         instance.run(); //run lines
  230.         objectMap.put(block, null);
  231.     }
  232.  
  233.     /**
  234.      * A line of code
  235.      */
  236.     private static class Line implements Comparable<Line> {
  237.         private final GotoInstance instance;
  238.         private final int line;
  239.         private final String label;
  240.         private final Runnable runnable;
  241.  
  242.         private Line(GotoInstance instance, int line, String label, Runnable runnable) {
  243.             this.instance = instance;
  244.             this.line = line;
  245.             this.label = label;
  246.             this.runnable = runnable;
  247.         }
  248.  
  249.         @Override
  250.         public boolean equals(Object o) {
  251.             if (this == o) return true;
  252.             if (!(o instanceof Line)) return false;
  253.  
  254.             Line line1 = (Line) o;
  255.  
  256.             if (line != line1.line) return false;
  257.             if (instance != line1.instance) return false; //intentionally not using .equals
  258.             return true;
  259.  
  260.         }
  261.  
  262.         @Override
  263.         public int hashCode() {
  264.             int result = instance.hashCode();
  265.             result = 31 * result + line;
  266.             return result;
  267.         }
  268.  
  269.         @Override
  270.         public int compareTo(Line o) {
  271.             return line - o.line;
  272.         }
  273.     }
  274.  
  275.     private static class Block {
  276.         private Object obj;
  277.         private int id;
  278.  
  279.         private Block(Object obj, int id) {
  280.             this.obj = obj;
  281.             this.id = id;
  282.         }
  283.  
  284.         @Override
  285.         public boolean equals(Object o) {
  286.             if (this == o) return true;
  287.             if (!(o instanceof Block)) return false;
  288.  
  289.             Block block = (Block) o;
  290.  
  291.             if (id != block.id) return false;
  292.             return obj == block.obj; //intentionally not using .equals
  293.  
  294.         }
  295.  
  296.         @Override
  297.         public int hashCode() {
  298.             int result = obj.hashCode();
  299.             result = 31 * result + id;
  300.             return result;
  301.         }
  302.     }
  303.  
  304.     /**
  305.      * Thrown when an object reference is null
  306.      */
  307.     private static class NullObjectException extends IllegalArgumentException {
  308.  
  309.     }
  310.  
  311.     /**
  312.      * Thrown when a code reference is null
  313.      */
  314.     private static class NullCodeException extends IllegalArgumentException {
  315.  
  316.     }
  317.  
  318.     /**
  319.      * Thrown when a label reference is null
  320.      */
  321.     private static class NullLabelException extends IllegalArgumentException {
  322.  
  323.     }
  324. }
Add Comment
Please, Sign In to add comment