OnJava8-Examples/enums/VendingMachine.java

180 lines
4.4 KiB
Java
Raw Normal View History

2015-09-07 11:44:36 -06:00
// enums/VendingMachine.java
2015-12-15 11:47:04 -08:00
// (c)2016 MindView LLC: see Copyright.txt
2015-11-15 15:51:35 -08:00
// We make no guarantees that this code is fit for any purpose.
// Visit http://mindviewinc.com/Books/OnJava/ for more book information.
2016-07-28 13:42:03 -06:00
// {java VendingMachine VendingMachineInput.txt}
2015-06-15 17:47:35 -07:00
import java.util.*;
2015-12-06 11:45:16 -08:00
import java.io.IOException;
2015-11-03 12:00:44 -08:00
import java.util.function.*;
2015-12-06 11:45:16 -08:00
import java.nio.file.*;
import java.util.stream.*;
2015-06-15 17:47:35 -07:00
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())
2015-11-03 12:00:44 -08:00
System.out.println(
"Insufficient money for " + selection);
2015-06-15 17:47:35 -07:00
else state = DISPENSING;
break;
case QUIT_TRANSACTION:
state = GIVING_CHANGE;
break;
case SHUT_DOWN:
state = TERMINAL;
default:
}
}
},
DISPENSING(StateDuration.TRANSIENT) {
@Override
void next() {
2015-11-03 12:00:44 -08:00
System.out.println("here is your " + selection);
2015-06-15 17:47:35 -07:00
amount -= selection.amount();
state = GIVING_CHANGE;
}
},
GIVING_CHANGE(StateDuration.TRANSIENT) {
@Override
void next() {
if(amount > 0) {
2015-11-03 12:00:44 -08:00
System.out.println("Your change: " + amount);
2015-06-15 17:47:35 -07:00
amount = 0;
}
state = RESTING;
}
},
TERMINAL {@Override
2015-11-03 12:00:44 -08:00
void output() { System.out.println("Halted"); } };
2015-06-15 17:47:35 -07:00
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");
}
2015-11-03 12:00:44 -08:00
void output() { System.out.println(amount); }
2015-06-15 17:47:35 -07:00
}
2015-11-03 12:00:44 -08:00
static void run(Supplier<Input> gen) {
2015-06-15 17:47:35 -07:00
while(state != State.TERMINAL) {
2015-11-03 12:00:44 -08:00
state.next(gen.get());
2015-06-15 17:47:35 -07:00
while(state.isTransient)
state.next();
state.output();
}
}
public static void main(String[] args) {
2015-11-03 12:00:44 -08:00
Supplier<Input> gen = new RandomInputSupplier();
2015-06-15 17:47:35 -07:00
if(args.length == 1)
2015-11-03 12:00:44 -08:00
gen = new FileInputSupplier(args[0]);
2015-06-15 17:47:35 -07:00
run(gen);
}
}
// For a basic sanity check:
2015-11-03 12:00:44 -08:00
class RandomInputSupplier implements Supplier<Input> {
2015-06-15 17:47:35 -07:00
@Override
2015-11-03 12:00:44 -08:00
public Input get() { return Input.randomSelection(); }
2015-06-15 17:47:35 -07:00
}
// Create Inputs from a file of ';'-separated strings:
2015-11-03 12:00:44 -08:00
class FileInputSupplier implements Supplier<Input> {
2015-06-15 17:47:35 -07:00
private Iterator<String> input;
2015-11-03 12:00:44 -08:00
public FileInputSupplier(String fileName) {
2015-12-06 11:45:16 -08:00
try {
input = Files.lines(Paths.get(fileName))
.skip(1) // Skip the comment line
.flatMap(s -> Arrays.stream(s.split(";")))
.map(String::trim)
.collect(Collectors.toList())
.iterator();
} catch(IOException e) {
throw new RuntimeException(e);
}
2015-06-15 17:47:35 -07:00
}
@Override
2015-11-03 12:00:44 -08:00
public Input get() {
2015-06-15 17:47:35 -07:00
if(!input.hasNext())
return null;
return Enum.valueOf(Input.class, input.next().trim());
}
2015-09-07 11:44:36 -06:00
}
/* Output:
2015-06-15 17:47:35 -07:00
25
50
2015-12-06 11:45:16 -08:00
75
here is your CHIPS
0
100
200
2015-06-15 17:47:35 -07:00
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
2015-09-07 11:44:36 -06:00
*/