1. 程式人生 > >Java-Java程式設計思想第四版 第十九章 列舉型別 練習

Java-Java程式設計思想第四版 第十九章 列舉型別 練習

練習1

// Use a static import to modify TrafficLight.java so you 
// don't have to qualify the enum instances.
import static enumerated.Signal.*;
public class Ja19_1{
  Signal color = RED;
  public void change() {
    switch(color) {
      // Note that you don't have to say RED
      // in the case statement:
      case RED:    color = GREEN;
                   break;
      case GREEN:  color = YELLOW;
                   break;
      case YELLOW: color = RED;
                   break;
    }
  }
  public String toString() {
    return "The traffic light is " + color;
  }
  public static void main(String[] args) {
    Ja19_1 t= new Ja19_1();
    for(int i = 0; i < 7; i++) {
      print(t);
      t.change();
    }
  }
}

練習2

// Instead of implementing an interface, make next() a static 
// method. What are the benefits and drawbacks of this approach?
//: enumerated/cartoons/EnumImplementation.java
// An enum can implement an interface
import java.util.*;
import net.mindview.util.*;
import static net.mindview.util.Print.*;

enum CartoonCharacter{
  SLAPPY, SPANKY, PUNCHY, SILLY, BOUNCY, NUTTY, BOB;
  public static CartoonCharacter next() {
    Random rand = new Random();
    return values()[rand.nextInt(values().length)];
  }
}

public class Ja19_2{
  public static <T> void printNext(Generator<T> rg) {
    System.out.print(rg.next() + ", ");
  }
  public static void main(String[] args) {
    // Choose any instance:
    CartoonCharacter cc = CartoonCharacter.BOB;
    for(int i = 0; i < 10; i++)
      print(cc.next());
  }
} 

練習3

// Add a new Course to Course.java and demonstrate that it works in Meal.java.
import net.mindview.util.*;
interface Food {
    enum Appetizer implements Food {
        SALAD, SOUP, SPRING_ROLLS;
    }
    enum MainCourse implements Food {
        LASAGNE, BURRITO, PAD_THAI,
        LENTILS, HUMMOUS, VINDALOO;
    }
    enum Dessert implements Food {
        TIRAMISU, GELATO, BLACK_FOREST_CAKE,
        FRUIT, CREME_CARAMEL;
    }
    enum Coffee implements Food {
        BLACK_COFFEE, DECAF_COFFEE, ESPRESSO,
        LATTE, CAPPUCCINO, TEA, HERB_TEA;
    }
    enum EE implements Food {
        A,B,C,D;
    }
}
enum Course {
    EE(Food.EE.class),
    APPETIZER(Food.Appetizer.class),
    MAINCOURSE(Food.MainCourse.class),
    DESSERT(Food.Dessert.class),
    COFFEE(Food.Coffee.class);
    private Food[] values;
    private Course(Class<? extends Food> kind) {
        values = kind.getEnumConstants();
    }
    public Food randomSelection() {
        return Enums.random(values);
    }
} ///:~
public class Ja19_3 {
    public static void main(String[] args) {
        for(int i = 0; i < 5; i++) {
            for(Course course : Course.values()) {
                Food food = course.randomSelection();
                System.out.println(food);
            }
            System.out.println("---");
        }
    }
}

練習4

// Repeat the above (EX 3) for Ja19_4.java.
import net.mindview.util.*;

public enum Ja19_4 {
    EE(Food.EE.class),
    APPETIZER(Food.Appetizer.class),
    MAINCOURSE(Food.MainCourse.class),
    DESSERT(Food.Dessert.class),
    COFFEE(Food.Coffee.class);
    private Food[] values;
    private Ja19_4(Class<? extends Food> kind) {
        values = kind.getEnumConstants();
    }
    public interface Food {
        enum Appetizer implements Food {
            SALAD, SOUP, SPRING_ROLLS;
        }
        enum MainCourse implements Food {
            LASAGNE, BURRITO, PAD_THAI,
            LENTILS, HUMMOUS, VINDALOO;
        }
        enum Dessert implements Food {
            TIRAMISU, GELATO, BLACK_FOREST_CAKE,
            FRUIT, CREME_CARAMEL;
        }
        enum Coffee implements Food {
            BLACK_COFFEE, DECAF_COFFEE, ESPRESSO,
            LATTE, CAPPUCCINO, TEA, HERB_TEA;
        }
        enum EE implements Food {
            A,B,C,D;
        }
    }
    public Food randomSelection() {
        return Enums.random(values);
    }
    public static void main(String[] args) {
        for(int i = 0; i < 5; i++) {
            for(Ja19_4 meal : Ja19_4.values()) {
                Food food = meal.randomSelection();
                System.out.println(food);
            }
            System.out.println("---");
        }
    }
}


練習5

/* Modify contol/VowelsAndConsonants.java so that it uses three enum types:
* VOWEL, SOMETIMES_A_VOWEL, and, CONSONANT. The enum constructor should
* take the various letters that describe that particular category. Hint: 
* use varargs, and remember that varargs automatically creates an array
* for you.
*/
import java.util.*;
import static net.mindview.util.Print.*;

enum A{
    VOWEL('a','o','e','i','u'),SOMETIMES_A_VOWEL('y','w'),CONSONANT('b', 'c', 'd', 'f', 'g', 'h', 'j', 'k', 'l', 'm', 'n', 'p', 'q', 'r', 's', 't', 'v', 'x', 'z');
    private Character[] ch;
    private A(Character... letters){
        ch=letters;
    }
    public static A letterType(Character c){
        if(Arrays.asList(VOWEL.ch).contains(c))return VOWEL;
        else if(Arrays.asList(SOMETIMES_A_VOWEL.ch).contains(c))return SOMETIMES_A_VOWEL;
        else return CONSONANT;
    }
}
public class Ja19_5{
    public static void main(String[] args){
        print(A.letterType('a').name());
    }
}

練習6

/* Is there any special benefit in nexting Appetizer, MainCourse, Dessert, and 
* Coffee inside Food rather than making them standalone enums that just happen
* to implement Food?
*/
//: enumerated/menu/Ja19_6.java
import net.mindview.util.*;

interface Food {}
enum Appetizer implements Food {
    SALAD, SOUP, SPRING_ROLLS;
}
enum MainCourse implements Food {
    LASAGNE, BURRITO, PAD_THAI,
    LENTILS, HUMMOUS, VINDALOO;
}
enum Dessert implements Food {
    TIRAMISU, GELATO, BLACK_FOREST_CAKE,
    FRUIT, CREME_CARAMEL;
}
enum Coffee implements Food {
    BLACK_COFFEE, DECAF_COFFEE, ESPRESSO,
    LATTE, CAPPUCCINO, TEA, HERB_TEA;
}
public enum Ja19_6 {
    APPETIZER(Appetizer.class),
    MAINCOURSE(MainCourse.class),
    DESSERT(Dessert.class),
    COFFEE(Coffee.class);
    private Food[] values;
    private Ja19_6(Class<? extends Food> kind) {
        values = kind.getEnumConstants();
    }
    public Food randomSelection() {
        return Enums.random(values);
    }
    public static void main(String[] args) {
        for(int i = 0; i < 5; i++) {
            for(Ja19_6 meal : Ja19_6.values()) {
                Food food = meal.randomSelection();
                System.out.println(food);
            }
            System.out.println("---");
        }
    }
} /* Same output as Meal.java *///:~


練習7

練習8

// Modify PostOffice.java so it has the ability to forward mail.
import java.util.*;
import net.mindview.util.*;
import static net.mindview.util.Print.*;

class Mail {
  // The NO's lower the probability of random selection:
  enum GeneralDelivery {YES,NO1,NO2,NO3,NO4,NO5}
  enum Scannability {UNSCANNABLE,YES1,YES2,YES3,YES4}
  enum Readability {ILLEGIBLE,YES1,YES2,YES3,YES4}
  enum Address {INCORRECT,OK1,OK2,OK3,OK4,OK5,OK6}
  enum ReturnAddress {MISSING,OK1,OK2,OK3,OK4,OK5}
  enum ForwardAddress {MISSING,OK1,OK2,OK3,OK4,OK5}
  GeneralDelivery generalDelivery;
  Scannability scannability;
  Readability readability;
  Address address;
  ReturnAddress returnAddress;
  ForwardAddress forwardAddress;
  static long counter = 0;
  long id = counter++;
  public String toString() { return "Mail " + id; }
  public String details() {
    return toString() +
      ", General Delivery: " + generalDelivery +
      ", Address Scanability: " + scannability +
      ", Address Readability: " + readability +
      ", Address Address: " + address +
      ", Return address: " + returnAddress+
      ", Forward address: " + forwardAddress;
  }
  // Generate test Mail:
  public static Mail randomMail() {
    Mail m = new Mail();
    m.generalDelivery= Enums.random(GeneralDelivery.class);
    m.scannability = Enums.random(Scannability.class);
    m.readability = Enums.random(Readability.class);
    m.address = Enums.random(Address.class);
    m.forwardAddress = Enums.random(ForwardAddress.class);
    m.returnAddress = Enums.random(ReturnAddress.class);
    return m;
  }
  public static Iterable<Mail> generator(final int count) {
    return new Iterable<Mail>() {
      int n = count;
      public Iterator<Mail> iterator() {
        return new Iterator<Mail>() {
          public boolean hasNext() { return n-- > 0; }
          public Mail next() { return randomMail(); }
          public void remove() { // Not implemented
            throw new UnsupportedOperationException();
          }
        };
      }
    };
  }
}

public class Ja19_8 {
  enum MailHandler {
    GENERAL_DELIVERY {
      boolean handle(Mail m) {
        switch(m.generalDelivery) {
          case YES:
            print("Using general delivery for " + m);
            return true;
          default: return false;
        }
      }
    },
    MACHINE_SCAN {
      boolean handle(Mail m) {
        switch(m.scannability) {
          case UNSCANNABLE: return false;
          default:
            switch(m.address) {
              case INCORRECT: 
                  switch(m.forwardAddress){
                      case MISSING: return false;
                      default:print("Delivering "+ m + " forward automatically");
                              return true;
                  }
              default:
                print("Delivering "+ m + " automatically");
                return true;
            }
        }
      }
    },
    VISUAL_INSPECTION {
      boolean handle(Mail m) {
        switch(m.readability) {
          case ILLEGIBLE: return false;
          default:
            switch(m.address) {
              case INCORRECT: 
                  switch(m.forwardAddress){
                      case MISSING: return false;
                      default:print("Delivering "+ m + " forward normally");
                              return true;
                  }
              default:
                print("Delivering " + m + " normally");
                return true;
            }
        }
      }
    },
    RETURN_TO_SENDER {
      boolean handle(Mail m) {
        switch(m.returnAddress) {
          case MISSING: return false;
          default:
            print("Returning " + m + " to sender");
            return true;
        }
      }
    };
    abstract boolean handle(Mail m);
  }
  static void handle(Mail m) {
    for(MailHandler handler : MailHandler.values())
      if(handler.handle(m))
        return;
    print(m + " is a dead letter");
  }
  public static void main(String[] args) {
    for(Mail mail : Mail.generator(10)) {
      print(mail.details());
      handle(mail);
      print("*****");
    }
  }
}

練習9

// Modify class PostOffice so that it uses an EnumMap.
import java.util.*;
import net.mindview.util.*;
import static net.mindview.util.Print.*;

class Mail {
    // The NO's lower the probability of random selection:
    enum GeneralDelivery {YES,NO1,NO2,NO3,NO4,NO5}
    enum Scannability {UNSCANNABLE,YES1,YES2,YES3,YES4}
    enum Readability {ILLEGIBLE,YES1,YES2,YES3,YES4}
    enum Address {INCORRECT,OK1,OK2,OK3,OK4,OK5,OK6}
    enum ReturnAddress {MISSING,OK1,OK2,OK3,OK4,OK5}
    GeneralDelivery generalDelivery;
    Scannability scannability;
    Readability readability;
    Address address;
    ReturnAddress returnAddress;
    static long counter = 0;
    long id = counter++;
    public String toString() { return "Mail " + id; }
    public String details() {
        return toString() +
            ", General Delivery: " + generalDelivery +
            ", Address Scanability: " + scannability +
            ", Address Readability: " + readability +
            ", Address Address: " + address +
            ", Return address: " + returnAddress;
    }
    // Generate test Mail:
    public static Mail randomMail() {
        Mail m = new Mail();
        m.generalDelivery= Enums.random(GeneralDelivery.class);
        m.scannability = Enums.random(Scannability.class);
        m.readability = Enums.random(Readability.class);
        m.address = Enums.random(Address.class);
        m.returnAddress = Enums.random(ReturnAddress.class);
        return m;
    }
    public static Iterable<Mail> generator(final int count) {
        return new Iterable<Mail>() {
            int n = count;
            public Iterator<Mail> iterator() {
                return new Iterator<Mail>() {
                    public boolean hasNext() { return n-- > 0; }
                    public Mail next() { return randomMail(); }
                    public void remove() { // Not implemented
                        throw new UnsupportedOperationException();
                    }
                };
            }
        };
    }
}
interface Handler{boolean handle(Mail m);}
public class Ja19_9 {
    enum MailHandler { GENERAL_DELIVERY, MACHINE_SCAN, VISUAL_INSPECTION, RETURN_TO_SENDER ; }
    public static void handle(Mail m,EnumMap<MailHandler,Handler> em) {
        for(Map.Entry<MailHandler,Handler> e: em.entrySet())
            if(e.getValue().handle(m))
                return;
        print(m + " is a dead letter");
    }
   public static void main(String[] args){ 
    EnumMap<MailHandler,Handler> em=new EnumMap<MailHandler,Handler>(MailHandler.class);
    em.put(MailHandler.GENERAL_DELIVERY,new Handler(){
        public boolean handle(Mail m) {
            switch(m.generalDelivery) {
                case YES:
                    print("Using general delivery for " + m);
                    return true;
                default: return false;
            }
        }
    });
    em.put(MailHandler.MACHINE_SCAN,new Handler(){
            public boolean handle(Mail m) {
                switch(m.scannability) {
                    case UNSCANNABLE: return false;
                    default:
                      switch(m.address) {
                          case INCORRECT: return false;
                          default:
                              print("Delivering "+ m + " automatically");
                              return true;
                      }
                }
            }
    });
    em.put(MailHandler.VISUAL_INSPECTION,new Handler(){
            public boolean handle(Mail m) {
                switch(m.readability) {
                    case ILLEGIBLE: return false;
                    default:
                                    switch(m.address) {
                                        case INCORRECT: return false;
                                        default:
                                                        print("Delivering " + m + " normally");
                                                        return true;
                                    }
                }
            }
    });
    em.put(MailHandler.RETURN_TO_SENDER,new Handler(){
            public boolean handle(Mail m) {
                switch(m.returnAddress) {
                    case MISSING: return false;
                    default:
                                  print("Returning " + m + " to sender");
                                  return true;
                }
            }
    });
        for(Mail mail : Mail.generator(10)) {
            print(mail.details());
            handle(mail,em);
            print("*****");
        }
}
}

練習10

// Modify class VendingMachine (only) using EnumMap so that one 
// program can have multiple instances of VendingMachine.
//import static enumerated.Input.*;
import java.util.*;
import net.mindview.util.*;
import static net.mindview.util.Print.*;
enum Input {
  NICKEL(5), DIME(10), QUARTER(25), DOLLAR(100),
  TOOTHPASTE(200), CHIPS(75), SODA(100), SOAP(50),
  ABORT_TRANSACTION {
    public int amount() { // Disallow
      throw new RuntimeException("ABORT.amount()");
    }
  },
  STOP { // This must be the last instance.
    public int amount() { // Disallow
      throw new RuntimeException("SHUT_DOWN.amount()");
    }
  };	
  int value; // In cents
  Input(int value) { this.value = value; }
  Input() {}
  public int amount() { return value; }; // In cents
  static Random rand = new Random(47);
  public static Input randomSelection() {
    // Don't include STOP:
    return values()[rand.nextInt(values().length - 1)];
  }
} ///:~

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,Category>(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);
	}
}

interface Command {  // In order to use a Command Design Pattern
	void next(Input input);
	void next();
}

enum State {
	RESTING,
	ADDING_MONEY,
	DISPENSING,
	GIVING_CHANGE,	
	TERMINAL
}

public class Ja19_10 {
	int id = ++count;
	static int count = 0; 
	State state = State.RESTING;
	int amount = 0; // for each transaction
	int banked = 0; // retained after transactions
	Input input = null;
	Input selection = null;
	boolean isTransient = false;
	// Enums must be static, sot use classes instead:
	class RestingDo implements Command {
		public void next(Input in) {
			isTransient = false;
			input = in;
			switch(Category.categorize(in)) {
				case MONEY:
					amount += in.amount();
					state = State.ADDING_MONEY;
					break;
				case SHUT_DOWN:
					state = State.TERMINAL;
				default:					
			}
		}
		public void next() { 
			isTransient = false; 			
		}
	}
	class AddingMoneyDo implements Command {
		public void next(Input input) {
			isTransient = false;
			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 = State.DISPENSING;
						isTransient = true;
					}
					break;
				case QUIT_TRANSACTION:
					state = State.GIVING_CHANGE;
					isTransient = true;
					break;
				case SHUT_DOWN:
					state = State.TERMINAL;
					banked = banked += amount;					
				default:
			}
		}
		public void next() { isTransient = false; }
	}
	class DispensingDo implements Command {
		public void next() {
			isTransient = true; 
			print("Here is your " + selection);
			state = State.GIVING_CHANGE;
		}
		public void next(Input input) { 
			isTransient = true; 
			print("Here is your " + selection);
			state = State.GIVING_CHANGE; 	
		}
	}
	class GivingChangeDo implements Command {
		public void next(Input input) { 
			isTransient = true; 
			if(amount > selection.amount()) {
				print("Your change: " + (amount - selection.amount()));					
			}
			banked = banked += selection.amount();
			amount = 0; // reset
			state = State.RESTING;					 
		}
		public void next() {
			isTransient = true; 
			if(amount < selection.amount())
				print("Returning your: " + amount); 
			if(amount > selection.amount()) {
				print("Your change: " + (amount - selection.amount()));
				banked = banked += selection.amount();					
			}
			if(amount == selection.amount())
				banked = banked += selection.amount();
			amount = 0;
			state = State.RESTING;
		}
	}
	class TerminalDo implements Command { 
		public void next(Input input) {
			print("state TERMINAL");
			isTransient = false;
		}
		public void next() {
			print("state TERMINAL");
			isTransient = false;			
		}		
	}
	Map<State,Command> em = 
		Collections.synchronizedMap(new EnumMap<State,Command>(State.class));
	Ja19_10() { // Load up the EnumMap in the constructor
		print("Ja19_10()#" + id); 	
		em.put(State.RESTING, new RestingDo());
		em.put(State.ADDING_MONEY, new AddingMoneyDo());
		em.put(State.DISPENSING, new DispensingDo());
		em.put(State.GIVING_CHANGE, new GivingChangeDo());
		em.put(State.TERMINAL, new TerminalDo());
	}
	void showAmount() { print("amount = " + amount); }
	void showBanked() { print("banked = " + banked); }
	public static void main(String[] args) {		
		Generator<Input> gen = new RandomInputGenerator();
		if(args.length == 1)
			gen = new FileInputGenerator10(args[0]);
		Ja19_10 vm10a = new Ja19_10();
		Ja19_10 vm10b = new Ja19_10();	
		Ja19_10 vm10c = new Ja19_10();
		print();
		print("Testing VendingMachine 10a:");
		while(vm10a.state != State.TERMINAL) {
			Input in = gen.next();
			(vm10a.em.get(vm10a.state)).next(in);
			while(vm10a.isTransient) {
				(vm10a.em.get(vm10a.state)).next();				
			}	
			vm10a.showAmount();							
		}
		vm10a.showBanked();
		print();
		print("Testing VendingMachine 10b:");
		gen = new FileInputGenerator10("Ja19_10bInput.txt");
		while(vm10b.state != State.TERMINAL) {
			Input in = gen.next();
			(vm10b.em.get(vm10b.state)).next(in);
			while(vm10b.isTransient) {
				(vm10b.em.get(vm10b.state)).next();				
			}	
			vm10b.showAmount();							
		}
		print();
		print("Testing VendingMachine 10c:");
		gen = new FileInputGenerator10("Ja19_10cInput.txt");
		while(vm10c.state != State.TERMINAL) {
			Input in = gen.next();
			(vm10c.em.get(vm10c.state)).next(in);
			while(vm10c.isTransient) {
				(vm10c.em.get(vm10c.state)).next();				
			}	
			vm10c.showAmount();							
		}
	}
}

// Create inputs from a file of ';'-separated strings;
class FileInputGenerator10 implements Generator<Input> {
	private Iterator<String> input;
	public FileInputGenerator10(String fileName) {
		input = new TextFile(fileName, ";").iterator();
	}
	public Input next() {
		if(input.hasNext()) {
			return Enum.valueOf(Input.class, input.next().trim());
		}
		return null;
	}	
}
class RandomInputGenerator implements Generator<Input> {
  public Input next() { return Input.randomSelection(); }
}



練習11

// enumerated/VendingMachine11.java
// TIJ4 Chapter Enumerated, Exercise 11, page 1047
/* In a real vending machine you will want to easily add and change
* the type of vended items, so the limits imposed by an enum on
* Input are impractical (remember that enums are for a restricted set 
* of types). Modify VendingMachine.java so that the vended items are
* represented by a class instead of being part of Input, and initialize
* an ArrayList of these objects from a text file 
* (using net.mindview.util.TextFile).
*/

/* My solution to one of the exercises in 
* Thinking in Java 4th Edition (by Bruce Eckel).
* It compiles and runs correctly using JDK 1.6.0
* @author Greg Gordon
* @author www.greggordon.org
* April, 2009
*/

/** Files required for input, in same package:
VendingMachineInput.txt
QUARTER;QUARTER;QUARTER;CHIPS;
DOLLAR; DOLLAR; TOOTHPASTE;
QUARTER; DIME; SODA;
QUARTER; DIME; NICKEL; SODA;
ABORT_TRANSACTION;
STOP;
VendingMachine11Input.txt:
Money:Nickel(5),Dime(10),Quarter(25),Dollar(100);Selection:Soda(100),Juice(125),
HotChocolate(100),Coffee(145),CandyBar(90);VendingEvent:AbortTransaction,Stop, 
**/

import java.util.*;
import net.mindview.util.*;
import static net.mindview.util.Print.*;

// Vending elements as classes implementing common interface:
interface VendingInput { String name(); } 

class MonetaryUnit implements VendingInput {
	private String name;
	private int amount = 0;
	MonetaryUnit(String name, int amount) {
		this.name = name;
		this.amount = amount;
	}
	public String name() { return name; }
	public int amount() { return amount; }
}

class VendedItem implements VendingInput {
	private String name;
	private int price = 0;
	VendedItem(String name, int price) {
		this.name = name;
		this.price = price;
	}
	public String name() { return name; }
	public int price() { return price; }
} 

class VendingEvent implements VendingInput {
	private String name;
	VendingEvent(String name) {
		this.name = name;
	}
	public String name() { return name; }	
}

public class Ja19_11 {
	private static State state = State.RESTING;
	private static int amount = 0;
	private static VendedItem selection = null;
	enum StateDuration { TRANSIENT } // Tagging enum
	enum State {
		RESTING {
			void next(VendingInput in) {
				if(MonetaryUnit.class.isInstance(in)) {
					amount += ((MonetaryUnit)in).amount();
					state = ADDING_MONEY;	
				}
				if(VendingEvent.class.isInstance(in)) {
					if(((VendingEvent)in).name().equals("Stop"))
						state = TERMINAL;
				}
			}
		},
		ADDING_MONEY {
			void next(VendingInput in) {
				if(MonetaryUnit.class.isInstance(in)) {
					amount += ((MonetaryUnit)in).amount();
				}				
				if(VendedItem.class.isInstance(in)) {
					selection = (VendedItem)in;
					if(amount < selection.price())
						print("Insufficient money for " + selection.name());
					else state = DISPENSING;
				}
				if(VendingEvent.class.isInstance(in)) {
					if(((VendingEvent)in).name().equals("AbortTransaction"))
						state = GIVING_CHANGE;
					if(((VendingEvent)in).name().equals("Stop"))
						state = TERMINAL;
				}				
			}
		},
		DISPENSING(StateDuration.TRANSIENT) {
			void next() {
				print("here is your " + selection.name());
				amount -= selection.price();
				state = GIVING_CHANGE;
			}
		},
		GIVING_CHANGE(StateDuration.TRANSIENT) {
			void next() {
				if(amount > 0) {
					print("Your change: " + amount);
					amount = 0;
				}
				state = RESTING;
			}
		},
		TERMINAL { void output() { print("Halted"); } };
		private boolean isTransient = false;
		State()	{} // no-arg constructor
		State(StateDuration trans) { isTransient = true; }
		void next(VendingInput in) {
			throw new RuntimeException("Only call " +
				"next(VendingInput in) for non-transient states");
		}
		void next() {	
			throw new RuntimeException("Only call next() for " +
				"StateDuration.TRANSIENT states");
		}
		void output() { print(amount); }
	}
	static void run(Generator<VendingInput> gen) {
		while(state != State.TERMINAL) {		
			state.next(((FileInputGenerator11)gen).next());
			while(state.isTransient) 
				state.next();
			state.output();					
		}
		state = State.RESTING;
		print();
	}
	static void runRandom(Generator<VendingInput> gen) {
		while(state != State.TERMINAL) {		
			state.next(((FileInputGenerator11)gen).randomNext());
			while(state.isTransient) 
				state.next();
			state.output();					
		}
		state = State.RESTING;
		print();				
	}
	static void runTextExample(Generator<VendingInput> gen) {
		while(state != State.TERMINAL) {	
		// for(int i = 0; i < 16; i++) {	
			state.next(((FileInputGenerator11)gen).textExampleNext("VendingMachineInput.txt"));
			while(state.isTransient) 
				state.next();
			state.output();					
		}
		state = State.RESTING;
		print();				
	}
	public static void main(String[] args) {
		FileInputGenerator11 gen = 
			new FileInputGenerator11("VendingMachine11Input.txt");
		runRandom(gen); // random vending inputs 
		run(gen); 
		runTextExample(gen); // inputs from VendingMachineInput.txt
	}	
}

class FileInputGenerator11 implements Generator<VendingInput> {
	private ArrayList<String> list;
	private List<VendingInput> vendList = new ArrayList<VendingInput>();
	private Iterator<VendingInput> it;
	private Random rand = new Random();
	private Iterator<String> input = new TextFile("VendingMachineInput.txt", ";").iterator();
	// Construct List of VendingInput from appropriately formatted txt file:
	public FileInputGenerator11(String fileName) { 
		list = new TextFile(fileName, ",|;|:");
		int m = list.indexOf("Money");
		int se = list.indexOf("Selection");
		int e = list.indexOf("VendingEvent");
		VendingInput vIn;
		for(String s : list) {
				int x = list.indexOf(s);
				if(m < x && x < se) {
					String[] sa = s.split("\\(|\\)");
					vIn = new MonetaryUnit(sa[0], Integer.parseInt(sa[1]));
					vendList.add(vIn);
				}
				else if(se < x && x < e) {
					String[] sa = s.split("\\(|\\)");
					vIn = new VendedItem(sa[0], Integer.parseInt(sa[1]));
					vendList.add(vIn);
				}
				else if(e < x) {
					vIn = new VendingEvent(s);
					vendList.add(vIn);
				}			
		}
		it = vendList.iterator();		
	}
	public VendingInput next() {
		if(list.isEmpty()) return null; 		
		if(it.hasNext()) {
			return it.next();			
		}
		return null;
	}
	public VendingInput randomNext() { 
		return vendList.get(rand.nextInt(vendList.size()));
	}
	public VendingInput textExampleNext(String fileName) {		
		if(!input.hasNext()) return null;
		String s = input.next().trim();
		String s1 = s.charAt(0) + s.substring(1).toLowerCase();
		for(int i = 0; i < this.vendList.size(); i++) {
			if(vendList.get(i).name().equals(s1)) { 
				return vendList.get(i);			
			}
		}
		return null;
	}	
}