OnJava8-Examples/enums/VendingMachine.java
Bruce Eckel 49edcc8b17 reorg
2015-06-15 17:47:35 -07:00

166 lines
4.0 KiB
Java

//: enums/VendingMachine.java
// ©2015 MindView LLC: see Copyright.txt
// {Args: VendingMachineInput.txt}
import java.util.*;
import com.mindviewinc.util.*;
import static com.mindviewinc.util.Print.*;
enum Category {
MONEY(Input.NICKEL, Input.DIME,
Input.QUARTER, Input.DOLLAR),
ITEM_SELECTION(Input.TOOTHPASTE, Input.CHIPS,
Input.SODA, Input.SOAP),
QUIT_TRANSACTION(Input.ABORT_TRANSACTION),
SHUT_DOWN(Input.STOP);
private Input[] values;
Category(Input... types) { values = types; }
private static EnumMap<Input,Category> 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<Input> gen) {
while(state != State.TERMINAL) {
state.next(gen.next());
while(state.isTransient)
state.next();
state.output();
}
}
public static void main(String[] args) {
Generator<Input> gen = new RandomInputGenerator();
if(args.length == 1)
gen = new FileInputGenerator(args[0]);
run(gen);
}
}
// For a basic sanity check:
class RandomInputGenerator implements Generator<Input> {
@Override
public Input next() { return Input.randomSelection(); }
}
// Create Inputs from a file of ';'-separated strings:
class FileInputGenerator implements Generator<Input> {
private Iterator<String> input;
public FileInputGenerator(String fileName) {
// Skip the comment line in the input file:
input = new TextFile(fileName, ";").listIterator(1);
}
@Override
public Input next() {
if(!input.hasNext())
return null;
return Enum.valueOf(Input.class, input.next().trim());
}
} /* Output:
25
50
Insufficient money for CHIPS
50
150
250
here is your TOOTHPASTE
Your change: 50
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
*///:~