//: enumerated/VendingMachine.java // {CompileTimeError} Not working in Java 8 // {Args: VendingMachineInput.txt} import java.util.*; import net.mindview.util.*; import static net.mindview.util.Print.*; enum Category { MONEY(NICKEL, DIME, QUARTER, DOLLAR), ITEM_SELECTION(TOOTHPASTE, CHIPS, SODA, SOAP), QUIT_TRANSACTION(ABORT_TRANSACTION), SHUT_DOWN(STOP); private Input[] values; Category(Input... types) { values = types; } private static EnumMap categories = new EnumMap<>(Input.class); static { for(Category c : Category.class.getEnumConstants()) for(Input type : c.values) categories.put(type, c); } public static Category categorize(Input input) { return categories.get(input); } } public class VendingMachine { private static State state = State.RESTING; private static int amount = 0; private static Input selection = null; enum StateDuration { TRANSIENT } // Tagging enum enum State { RESTING { @Override void next(Input input) { switch(Category.categorize(input)) { case MONEY: amount += input.amount(); state = ADDING_MONEY; break; case SHUT_DOWN: state = TERMINAL; default: } } }, ADDING_MONEY { @Override void next(Input input) { switch(Category.categorize(input)) { case MONEY: amount += input.amount(); break; case ITEM_SELECTION: selection = input; if(amount < selection.amount()) print("Insufficient money for " + selection); else state = DISPENSING; break; case QUIT_TRANSACTION: state = GIVING_CHANGE; break; case SHUT_DOWN: state = TERMINAL; default: } } }, DISPENSING(StateDuration.TRANSIENT) { @Override void next() { print("here is your " + selection); amount -= selection.amount(); state = GIVING_CHANGE; } }, GIVING_CHANGE(StateDuration.TRANSIENT) { @Override void next() { if(amount > 0) { print("Your change: " + amount); amount = 0; } state = RESTING; } }, TERMINAL {@Override void output() { print("Halted"); } }; private boolean isTransient = false; State() {} State(StateDuration trans) { isTransient = true; } void next(Input input) { throw new RuntimeException("Only call " + "next(Input input) for non-transient states"); } void next() { throw new RuntimeException("Only call next() for " + "StateDuration.TRANSIENT states"); } void output() { print(amount); } } static void run(Generator gen) { while(state != State.TERMINAL) { state.next(gen.next()); while(state.isTransient) state.next(); state.output(); } } public static void main(String[] args) { Generator gen = new RandomInputGenerator(); if(args.length == 1) gen = new FileInputGenerator(args[0]); run(gen); } } // For a basic sanity check: class RandomInputGenerator implements Generator { @Override public Input next() { return Input.randomSelection(); } } // Create Inputs from a file of ';'-separated strings: class FileInputGenerator implements Generator { private Iterator input; public FileInputGenerator(String fileName) { input = new TextFile(fileName, ";").iterator(); } @Override public Input next() { if(!input.hasNext()) return null; return Enum.valueOf(Input.class, input.next().trim()); } } /* Output: 25 50 75 here is your CHIPS 0 100 200 here is your TOOTHPASTE 0 25 35 Your change: 35 0 25 35 Insufficient money for SODA 35 60 70 75 Insufficient money for SODA 75 Your change: 75 0 Halted *///:~