欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

【实验】异常和IO在Gourmet咖啡系统中的应用

程序员文章站 2022-04-04 23:31:07
...

前尘往事

面向对象设计、UML建模与基本的Java面向对象编程实现
实验一:设计模式在Gourmet咖啡系统中的应用

Using File I/O in the Gourmet Coffee System

Prerequisites, Goals, and Outcomes

Prerequisites: Before you begin this exercise, you need mastery of the following:

  • Java API
    • Knowledge of the class StringTokenizer
  • File I/O
    • Knowledge of file I/O
      • How to read data from a file
      • How to write data to a file

Goals: Reinforce your ability to use file I/O

Outcomes: You will master the following skills:

  • Produce applications that read data from a file and parse it
  • Produce applications that write data to a file

Background
In this assignment, you will create another version of the Gourmet Coffee System. In previous versions, the data for the product catalog was hard-coded in the application. In this version, the data will be loaded from a file. Also, the user will be able to write the sales information to a file in one of three formats: plain text, HTML, or XML. Part of the work has been done for you and is provided in the student archive. You will implement the code that loads the product catalog and persists the sales information.

Description
The Gourmet Coffee System sells three types of products: coffee, coffee brewers, and accessories for coffee consumption. A file called catalog.dat stores the product data:

  • catalog.dat. File with product data

Every line in catalog.dat contains exactly one product.
A line for a coffee product has the following format:
Coffee_code_description_price_origin_roast_flavor_aroma_acidity_body

where:

  • Coffee” is a prefix that indicates the line type.
  • code is a string that represents the code of the coffee.
  • description is a string that represents the description of the coffee.
  • price is a double that represents the price of the coffee.
  • origin is a string that represents the origin of the coffee.
  • roast is a string that represents the roast of the coffee.
  • flavor is a string that represents the flavor of the coffee.
  • aroma is a string that represents the aroma of the coffee.
  • acidity is a string that represents the acidity of the coffee.
  • body is a string that represents the body of the coffee.

The fields are delimited by an underscore ( _ ). You can assume that the fields themselves do not contain any underscores.
A line for a coffee brewer has the following format:
Brewer_code_description_price_model_waterSupply_numberOfCups

where:

  • Brewer” is a prefix that indicates the line type.
  • code is a string that represents the code of the brewer.
  • description is a string that represents the description of the brewer.
  • price is a double that represents the price of the brewer.
  • model is a string that represents the model of the coffee brewer.
  • waterSupply is a string that represents the water supply of the coffee brewer.
  • numberOfCups is an integer that represents the capacity of the coffee brewer in number of cups.

The fields are delimited by an underscore ( _ ). You can assume that the fields themselves do not contain any underscores.
A line for a coffee accessory has the following format:
Product_code_description_price

where:

  • Product” is a prefix that indicates the line type.
  • code is a string that represents the code of the product.
  • description is a string that represents the description of the product.
  • price is a double that represents the price of the product.

The fields are delimited by an underscore ( _ ). You can assume that the fields themselves do not contain any underscores.
The following class diagram highlights the elements you will use to load the product catalog and persist the sales information:

【实验】异常和IO在Gourmet咖啡系统中的应用
Figure 1 Portion of Gourmet Coffee System class diagram

In this assignment, you will implement FileCatalogloader and complete the implementation of GourmetCoffee.

Interface CatalogLoader

The interface CatalogLoader declares a method for producing a product catalog. A complete implementation of this interface is provided in the student archive.

Method:

  • Catalog loadCatalog(String fileName) throws FileNotFoundException, IOException, DataFormatException

Loads the information in the specified file into a product catalog and returns the catalog.

Class DataFormatException

This exception is thrown when a line in the file being parsed has errors:

  • The line does not have the expected number of tokens.
  • The tokens that should contain numbers do not.

A complete implementation of this class is provided in the student archive.

Class FileCatalogLoader

The class FileCatalogLoader implements interface CatalogLoader. It is used to obtain a product catalog from a file. You should implement this class from scratch:

Methods:

  • private Product readProduct(String line) throws DataFormatException
    This method reads a line of coffee-accessory data. It uses the class StringTokenizer to extract the accessory data in the specified line. If the line is error free, this method returns a Product object that encapsulates the accessory data. If the line has errors, that is, if it does not have the expected number of tokens or the token that should contain a double does not; this method throws a DataFormatException that contains the line of malformed data.

  • private Coffee readCoffee(String line) throws DataFormatException
    This method reads a line of coffee data. It uses the class StringTokenizer to extract the coffee data in the specified line. If the line is error free, this method returns a Coffee object that encapsulates the coffee data. If the line has errors, that is, if it does not have the expected number of tokens or the token that should contain a double does not; this method throws a DataFormatException that contains the line of malformed data.

  • private CoffeeBrewer readCoffeeBrewer(String line) throws DataFormatException
    This method reads a line of coffee-brewer data. It uses the class StringTokenizer to extract the brewer data in the specified line. If the line is error free, this method returns a CoffeeBrewer object that encapsulates the brewer data. If the line has errors, that is, if it does not have the expected number of tokens or the tokens that should contain a number do not; this method throws a DataFormatException that contains the line of malformed data.

  • public Catalog loadCatalog(String filename) throws FileNotFoundException, IOException, DataFormatException
    This method loads the information in the specified file into a product catalog and returns the catalog. It begins by opening the file for reading. It then proceeds to read and process each line in the file. The method String.startsWith is used to determine the line type:

    • If the line type is “Product”, the method readProduct is invoked.
    • If the line type is “Coffee”, the method readCoffee is invoked.
    • If the line type is “Brewer”, the method readCoffeeBrewer is invoked.

    After the line is processed, loadCatalog adds the product (accessory, coffee, or brewer) to the product catalog. When all the lines in the file have been processed, load returns the product catalog to the calling method.
    This method can throw the following exceptions:

    • FileNotFoundException — if the specified file does not exist.
    • IOException — if there is an error reading the information in the specified file.
    • DataFormatException — if a line in the file has errors (the exception should contain the line of malformed data).

Class GourmetCoffee

A partial implementation of class GourmetCoffee is provided in the student archive. You should implement writeFile, a method that writes sales information to a file:

  • private void writeFile(String fileName, String content) throws IOException

This method creates a new file with the specified name, writes the specified string to the file, and then closes the file.

Class TestFileCatalogLoader

This class is a test driver for FileCatalogLoader. A complete implementation is included in the student archive student-files.zip. You should use this class to test your implementation of FileCatalogLoader.

Files
The following files are needed to complete this assignment:

  • student-files.zip — Download this file. This archive contains the following:
    • Class files
      • Coffee.class
      • CoffeeBrewer.class
      • Product.class
      • Catalog.class
      • OrderItem.class
      • Order.class
      • Sales.class
      • SalesFormatter.class
      • PlainTextSalesFormatter.class
      • HTMLSalesFormatter.class
      • XMLSalesFormatter.class
    • Documentation
      • Coffee.html
      • CoffeeBrewer.html
      • Product.html
      • Catalog.html
      • OrderItem.html
      • Order.html
      • Sales.html
      • SalesFormatter.html
      • PlainTextSalesFormatter.html
      • HTMLSalesFormatter.html
      • XMLSalesFormatter.html
    • Java files
      • CatalogLoader.java: A complete implementation
      • DataFormatException.java: A complete implementation
      • TestFileCatalogLoader.java: A complete implementation
      • GourmetCoffee.java: Use this template to complete your implementation.
    • Data files for the test driver
      • catalog.dat: A file with product information
      • empty.dat: An empty file

Tasks
Implement the class FileCatalogLoader and the method GourmetCoffee.writeFile. Document using Javadoc and follow Sun’s code conventions. The following steps will guide you through this assignment. Work incrementally and test each increment. Save often.

  • Extract the files by issuing the following command at the command prompt:
    C:>unzip student-files.zip

  • Then, implement class FileCatalogLoader from scratch. Use the TestFileCatalogLoader driver to test your implementation.

  • Next, implement the method GourmetCoffee.writeFile.

  • Finally, compile the class GourmetCoffee, and execute the class GourmetCoffee by issuing the following command at the command prompt:
    C:>java GourmetCoffee catalog.dat
    Sales information has been hard-coded in the GourmetCoffee template provided by iCarnegie.

  • If the user displays the catalog, the output should be:
    C001 Colombia, Whole, 1 lb
    C002 Colombia, Ground, 1 lb
    C003 Italian Roast, Whole, 1 lb
    C004 Italian Roast, Ground, 1 lb
    C005 French Roast, Whole, 1 lb
    C006 French Roast, Ground, 1 lb
    C007 Guatemala, Whole, 1 lb
    C008 Guatemala, Ground, 1 lb
    C009 Sumatra, Whole, 1 lb
    C010 Sumatra, Ground, 1 lb
    C011 Decaf Blend, Whole, 1 lb
    C012 Decaf Blend, Ground, 1 lb
    B001 Home Coffee Brewer
    B002 Coffee Brewer, 2 Warmers
    B003 Coffee Brewer, 3 Warmers
    B004 Commercial Coffee, 20 Cups
    B005 Commercial Coffee, 40 Cups
    A001 Almond Flavored Syrup
    A002 Irish Creme Flavored Syrup
    A003 Mint Flavored syrup
    A004 Caramel Flavored Syrup
    A005 Gourmet Coffee Cookies
    A006 Gourmet Coffee Travel Thermo
    A007 Gourmet Coffee Ceramic Mug
    A008 Gourmet Coffee 12 Cup Filters
    A009 Gourmet Coffee 36 Cup Filters

  • If the user saves the sales information in plain text, a file with the following content should be created:

------------------------
Order 1

5 C001 17.99

Total = 89.94999999999999
------------------------
Order 2

2 C002 18.75
2 A001 9.0

Total = 55.5
------------------------
Order 3

1 B002 200.0

Total = 200.0
  • If the user saves the sales information in HTML, a file with the following content should be created:
<html>
<body>
<center><h2>Orders</h2></center>
<hr>
<h4>Total = 89.94999999999999</h4>
<p>
<b>code:</b> C001<br>
<b>quantity:</b> 5<br>
<b>price:</b> 17.99
</p>
<hr>
<h4>Total = 55.5</h4>
<p>
<b>code:</b> C002<br>
<b>quantity:</b> 2<br>
<b>price:</b> 18.75
</p>
<p>
<b>code:</b> A001<br>
<b>quantity:</b> 2<br>
<b>price:</b> 9.0
</p>
<hr>
<h4>Total = 200.0</h4>
<p>
<b>code:</b> B002<br>
<b>quantity:</b> 1<br>
<b>price:</b> 200.0
</p>
</body>
</html>
  • If the user saves the sales information in XML, a file with the following content should be created:
<Sales>
<Order total="89.94999999999999">
<OrderItem quantity="5" price="17.99">C001</OrderItem>
</Order>
<Order total="55.5">
<OrderItem quantity="2" price="18.75">C002</OrderItem>
<OrderItem quantity="2" price="9.0">A001</OrderItem>
</Order>
<Order total="200.0">
<OrderItem quantity="1" price="200.0">B002</OrderItem>
</Order>
</Sales>

Submission
Upon completion, submit only the following:

  1. FileCatalogLoader.java
  2. GourmetCoffee.java

提供的.class文件

这真是不可描述。。。
下面的代码是****←_←反编译得到的.java,eclipse装插件、idea自身都可以支持简单不加密的****。
反编译出来的还是比较low的,比如泛型啥的都没有啊!

Product类

public class Product {
	private String code;
	private String description;
	private double price;

	public Product(String var1, String var2, double var3) {
		this.code = var1;
		this.description = var2;
		this.price = var3;
	}

	public String getCode() {
		return this.code;
	}

	public String getDescription() {
		return this.description;
	}

	public double getPrice() {
		return this.price;
	}

	public boolean equals(Object var1) {
		return var1 instanceof Product && this.getCode().equals(((Product)var1).getCode());
	}

	public String toString() {
		return this.getCode() + "_" + this.getDescription() + "_" + this.getPrice();
	}
}

Coffee类

public class Coffee extends Product {
	private String origin;
	private String roast;
	private String flavor;
	private String aroma;
	private String acidity;
	private String body;

	public Coffee(String var1, String var2, double var3, String var5, String var6, String var7, String var8, String var9, String var10) {
		super(var1, var2, var3);
		this.origin = var5;
		this.roast = var6;
		this.flavor = var7;
		this.aroma = var8;
		this.acidity = var9;
		this.body = var10;
	}

	public String getOrigin() {
		return this.origin;
	}

	public String getRoast() {
		return this.roast;
	}

	public String getFlavor() {
		return this.flavor;
	}

	public String getAroma() {
		return this.aroma;
	}

	public String getAcidity() {
		return this.acidity;
	}

	public String getBody() {
		return this.body;
	}

	public String toString() {
		return super.toString() + "_" + this.getOrigin() + "_" + this.getRoast() + "_" + this.getFlavor() + "_" + this.getAroma() + "_" + this.getAcidity() + "_" + this.getBody();
	}
}

CoffeeBrewer类

public class CoffeeBrewer extends Product {
	private String model;
	private String waterSupply;
	private int numberOfCups;

	public CoffeeBrewer(String var1, String var2, double var3, String var5, String var6, int var7) {
		super(var1, var2, var3);
		this.model = var5;
		this.waterSupply = var6;
		this.numberOfCups = var7;
	}

	public String getModel() {
		return this.model;
	}

	public String getWaterSupply() {
		return this.waterSupply;
	}

	public int getNumberOfCups() {
		return this.numberOfCups;
	}

	public String toString() {
		return super.toString() + "_" + this.getModel() + "_" + this.getWaterSupply() + "_" + this.getNumberOfCups();
	}
}

OrderItem类

public class OrderItem {
	private Product product;
	private int quantity;

	public OrderItem(Product var1, int var2) {
		this.product = var1;
		this.quantity = var2;
	}

	public Product getProduct() {
		return this.product;
	}

	public int getQuantity() {
		return this.quantity;
	}

	public void setQuantity(int var1) {
		this.quantity = var1;
	}

	public double getValue() {
		return this.getProduct().getPrice() * (double)this.getQuantity();
	}

	public String toString() {
		return this.getQuantity() + " " + this.getProduct().getCode() + " " + this.getProduct().getPrice();
	}
}

Order类

import java.util.ArrayList;
import java.util.Iterator;

public class Order implements Iterable<OrderItem> {
	private ArrayList<OrderItem> items = new ArrayList<>();

	public Order() {
	}

	public void addItem(OrderItem var1) {
		this.items.add(var1);
	}

	public void removeItem(OrderItem var1) {
		this.items.remove(var1);
	}

	public Iterator<OrderItem> iterator() {
		return this.items.iterator();
	}

	public OrderItem getItem(Product var1) {
		Iterator var2 = this.items.iterator();

		OrderItem var3;
		do {
			if (!var2.hasNext()) {
				return null;
			}

			var3 = (OrderItem)var2.next();
		} while(!var3.getProduct().equals(var1));

		return var3;
	}

	public int getNumberOfItems() {
		return this.items.size();
	}

	public double getTotalCost() {
		double var1 = 0.0D;

		OrderItem var4;
		for(Iterator var3 = this.items.iterator(); var3.hasNext(); var1 += var4.getValue()) {
			var4 = (OrderItem)var3.next();
		}

		return var1;
	}
}

Catalog类

import java.util.ArrayList;
import java.util.Iterator;

public class Catalog implements Iterable<Product> {
	private ArrayList<Product> products = new ArrayList<>();

	public Catalog() {
	}

	public void addProduct(Product var1) {
		this.products.add(var1);
	}

	public Iterator<Product> iterator() {
		return this.products.iterator();
	}

	public Product getProduct(String var1) {
		Iterator var2 = this.products.iterator();

		Product var3;
		do {
			if (!var2.hasNext()) {
				return null;
			}

			var3 = (Product)var2.next();
		} while(!var3.getCode().equals(var1));

		return var3;
	}

	public int getNumberOfProducts() {
		return this.products.size();
	}
}

Sales类

import java.util.ArrayList;
import java.util.Iterator;

public class Sales implements Iterable<Order> {
	private ArrayList<Order> orders = new ArrayList<>();

	public Sales() {
	}

	public void addOrder(Order var1) {
		this.orders.add(var1);
	}

	public Iterator<Order> iterator() {
		return this.orders.iterator();
	}

	public int getNumberOfOrders() {
		return this.orders.size();
	}
}

SalesFormatter类

public interface SalesFormatter {
    
    //Produces a string representation of the sales information.
    public abstract String formatSales(Sales sales);

}

PlainTextSalesFormatter类

import java.util.Iterator;

public class PlainTextSalesFormatter implements SalesFormatter {
    
    private static PlainTextSalesFormatter singletonInstance;   
    
    private PlainTextSalesFormatter() {   
    }   
       
    public static synchronized PlainTextSalesFormatter getSingletonInstance() {   
        if (singletonInstance == null)   
            singletonInstance = new PlainTextSalesFormatter();   
        return singletonInstance;   
    }   
       
    public String formatSales(Sales sales) {   
        String string = "";   
        int i = 1;
        Iterator<Order> iterator1 = sales.iterator();
        while(iterator1.hasNext()) {
            Order order = iterator1.next();
            string +="---------------------\r\n";   
            string += "Order " + i + "\r\n\r\n"; 
            Iterator<OrderItem> iterator2 = order.iterator();
            while(iterator2.hasNext()) {
                OrderItem orderItem = iterator2.next();
                string += orderItem.getQuantity() + " " + orderItem.getProduct().getCode() + " " +orderItem.getProduct().getPrice() + "\r\n";
            }
            i++;   
            string += "\r\n" + "Total = " + order.getTotalCost() + "\r\n"; 
        }  
        return string;   
    }   

}

HTMLSalesFormatter类

import java.util.Iterator;

public class HTMLSalesFormatter implements SalesFormatter {
    
    private static HTMLSalesFormatter singletonInstance;   
    
    private HTMLSalesFormatter() {   
    }   
        
    public static synchronized HTMLSalesFormatter getSingletonInstance() {   
        if (singletonInstance == null)   
            singletonInstance = new HTMLSalesFormatter();   
        return singletonInstance;   
    }   
       
    public String formatSales(Sales sales) {   
        String string = "";   
        string +="<html>\r\n  <body>\r\n    <center><h2>Orders</h2></center>\r\n";
        Iterator<Order> iterator1 = sales.iterator();
        while(iterator1.hasNext()) {
            Order order = iterator1.next();
            string += "    <hr>\r\n    <h4>Total = " + order.getTotalCost() + "</h4>\r\n      <p>\r\n";  
            Iterator<OrderItem> iterator2 = order.iterator();
            while(iterator2.hasNext()) {
                OrderItem orderItem = iterator2.next();
                string += "        <b>code:</b> "+orderItem.getProduct().getCode()+"<br>\r\n" + "        <b>quantity:</b> "+orderItem.getQuantity()+"<br>\r\n"+"        <b>price:</b> "+orderItem.getProduct().getPrice()+"\r\n";
            }  
            string += "      </p>\r\n";
        }  
        string += "  </body>\r\n</html>\r\n";
        return string;  
    }

}

XMLSalesFormatter类

import java.util.Iterator;

public class XMLSalesFormatter implements SalesFormatter {
    
    private static XMLSalesFormatter singletonInstance;   
    
    private XMLSalesFormatter() {   
    }   
       
    public static synchronized XMLSalesFormatter getSingletonInstance() {   
        if (singletonInstance == null)   
            singletonInstance = new XMLSalesFormatter();   
        return singletonInstance;   
    }   
       
    public String formatSales(Sales sales) {   
        String string = "";   
        string +="<Sales>\r\n"; 
        Iterator<Order> iterator1 = sales.iterator();
        while(iterator1.hasNext()) {
            Order order = iterator1.next();
            string += "  <Order total=\""+order.getTotalCost()+"\">\r\n";  
            Iterator<OrderItem> iterator2 = order.iterator();
            while(iterator2.hasNext()) {
                OrderItem orderItem = iterator2.next();
                string += "    <OrderItem quantity=\""+orderItem.getQuantity()+"\" price=\""+orderItem.getProduct().getPrice()+"\">"+orderItem.getProduct().getCode()+"</OrderItem>\r\n";
            }  
            string += "  </Order>\r\n";
        }  
        string += "</Sales>\r\n";
        return string;     
    }   

}

Code

DataFormatException类

public class DataFormatException extends Exception  {
    
    //序列化
    private static final long serialVersionUID = 1L;

	/**
	 * Constructs a <code>DataFormatException</code> with no detail
	 * message.
	 */
	public DataFormatException() {

	}

	/**
	 * Constructs a <code>DataFormatException</code> with the
	 * specified detail message.
	 *
	 * @param message  the malformed data
	 */
	public DataFormatException(String message) {

		super(message);
	}
}

CatalogLoader类

import java.io.*;

/**
 * This interface declares a method for obtaining a products catalog.
 *
 * @author BlankSpace
 * @version 1.1.0
 * @see Catalog
 */
public interface CatalogLoader  {

	/*
	 * Loads the product catalog with the data in the specified file.
	 *
	 * @param filename  The name of a file that contains catalog
	 *                  information.
	 * @return the product catalog.
	 * @throws FileNotFoundException  if the specified file does not
	 *                                exist.
	 * @throws IOException  if there is an error reading the
	 *                     information in the specified file.
	 * @throws DataFormatException  if the file contains
	 *                                     badly-formed data.
	 */
	Catalog loadCatalog(String fileName)
		throws FileNotFoundException, IOException, DataFormatException;
}

FileCatalogLoader类

import java.util.StringTokenizer; 
import java.io.*;

/**
 * This class implements interface CatalogLoader. It is used to obtain a product catalog from a file.
 * 
 * @author BlankSpace
 * @version 1.0.0
 * @see DataFormatException
 */
public class FileCatalogLoader implements CatalogLoader {
    private final String split = "_";

    /**
     * This method reads a line of coffee-accessory data.
     * It uses the class StringTokenizer to extract the accessory data in the specified line.
     * If the line is error free, this method returns a Product object that encapsulates the accessory data.
     * If the line has errors, that is, if it does not have the expected number of tokens or the token that should contain a double does not.
     * This method throws a DataFormatException that contains the line of malformed data. 
     * @param line
     * @return an new object of Coffee
     * @throws DataFormatException, which contains the line of malformed data
     */
    private Product readProduct(String line) throws DataFormatException {   
        String code = null;   
        String description = null;   
        double price = 0.0;   
        StringTokenizer token = new StringTokenizer(line, split);   
        while (token.hasMoreTokens()) {   
            token.nextToken();   
            code = token.nextToken();   
            description = token.nextToken();   
            price = Double.parseDouble(token.nextToken());   
        }   
        return new Product(code, description, price);   
    }   
    
    /**
     * This method reads a line of coffee data. It uses the class StringTokenizer to extract the coffee data in the specified line.
     * If the line is error free, this method returns a Coffee object that encapsulates the coffee data.
     * If the line has errors, that is, if it does not have the expected number of tokens or the token that should contain a double does not.
     * This method throws a DataFormatException that contains the line of malformed data. 
     * @param line
     * @return an new object of Coffee
     * @throws DataFormatException, which contains the line of malformed data
     */
    private Coffee readCoffee(String line) throws DataFormatException {   
        String code = null;   
        String description = null;   
        double price = 0.0;   
        String origin = null;   
        String roast = null;   
        String flavor = null;   
        String aroma = null;   
        String acidity = null;   
        String body = null;   
        StringTokenizer token = new StringTokenizer(line, split);   
        while (token.hasMoreTokens()) {   
            token.nextToken();   
            code = token.nextToken();   
            description = token.nextToken();   
            price = Double.parseDouble(token.nextToken());   
            origin = token.nextToken();   
            roast = token.nextToken();   
            flavor = token.nextToken();   
            aroma = token.nextToken();   
            acidity = token.nextToken();   
            body = token.nextToken();   
               
        }   
        return new Coffee(code, description, price, origin, roast, flavor, aroma, acidity, body);   
    }
    
   /**
    * This method reads a line of coffee-brewer data.
    * It uses the class StringTokenizer to extract the brewer data in the specified line. 
    * If the line is error free, this method returns a CoffeeBrewer object that encapsulates the brewer data.
    * If the line has errors, that is, if it does not have the expected number of tokens or the tokens that should contain a number do not.
    * This method throws a DataFormatException that contains the line of malformed data. 
    * @param line
    * @return an new object of Coffee
    * @throws DataFormatException, which contains the line of malformed data
    */
    private CoffeeBrewer readCoffeeBrewer(String line) throws DataFormatException {   
        String code = null;   
        String description = null;   
        double price = 0.0;   
        String model = null;   
        String waterSupply = null;   
        int numberOfCups = 0;   
        StringTokenizer token = new StringTokenizer(line, split);   
        while (token.hasMoreTokens()) {   
            token.nextToken();   
            code = token.nextToken();   
            description = token.nextToken();   
            price = Double.parseDouble(token.nextToken());   
            model = token.nextToken();   
            waterSupply = token.nextToken();   
            numberOfCups = Integer.parseInt(token.nextToken());               
        }   
        return new CoffeeBrewer(code, description, price, model, waterSupply, numberOfCups);   
    }   
    
    @Override
    public Catalog loadCatalog(String filename) throws FileNotFoundException, IOException, DataFormatException {   
        Catalog catalog = new Catalog();   
        BufferedReader buffer = new BufferedReader(new FileReader(filename));    
        String line = buffer.readLine();
        
        while (line != null) {   
            if (line.startsWith("Product")) {   
                catalog.addProduct(readProduct(line));   
            } else if(line.startsWith("Coffee")) {   
                catalog.addProduct(readCoffee(line));   
            } else if(line.startsWith("Brewer")) {   
                catalog.addProduct(readCoffeeBrewer(line));   
            }   
            line = buffer.readLine();   
        }   
        buffer.close();   
        return catalog;   
    }   

}

TestFileCatalogLoader类

import java.io.*;
import java.util.*;

/**
 * Tests the class <code>FileCatalogLoader</code>
 *
 * @author BlankSpace
 * @version 1.0.0
 * @see FileCatalogLoader
 */
public class  TestFileCatalogLoader  {

	/* Standard output stream */
	private static PrintWriter  stdOut = new  PrintWriter(System.out, true);

	/* Standard error stream */
	private static PrintWriter  stdErr = new  PrintWriter(System.err, true);

	/**
	 * Tests methods of class {@link FileCatalogLoader}
	 *
	 * @param args  not used.
	 * @throws IOException  if an I/O error occurs.
	 */
	public static void main (String args[]) throws IOException {

		stdOut.println("");
		stdOut.println("Testing class FileCatalogLoader...");

		TestFileCatalogLoader tester =
			new TestFileCatalogLoader();

		tester.testLoadCatalog();
		stdOut.println("All tests passed");
		stdOut.println("");
	}

	/**
	 * Displays a message in the standard error stream if the value specified
	 * by parameter <code>condition<code> is <code>false</code>.
	 *
	 * @param message  the error message.
	 * @param condition  the test condition.
	 * @return the value of <code>condition</code>
	 */
	public static void assertTrue(String message, boolean condition) {

		if (!condition) {
			stdErr.print("** Test failure ");
			stdErr.println(message);

			System.exit(1);
		}

	}
	
	/**
	 * Displays a message in the standard error stream.
	 *
	 * @param message  the error message.
	 * @return <code>false</code>;
	 */
	public static void fail(String message) {

		stdErr.print("** Test failure ");
		stdErr.println(message);

		System.exit(1);
	}

	/**
	 * Tests the method <code>loadCatalog</code>.
	 *
	 * @return <code>true</code> if all test passed; otherwise returns
	 *         <code>false</code>.
	 * @throws IOException  if an I/O error occurs.
	 */
	public void testLoadCatalog() throws IOException {

		CatalogLoader loader = new FileCatalogLoader();
			
		try {
			// Testing an empty file
			Catalog emptyCatalog =
				loader.loadCatalog("empty.dat");
				
			assertTrue("1, testing method read with an empty file",
				emptyCatalog instanceof Catalog);
			assertTrue("2, testing method read with an empty file"
				+ emptyCatalog.getNumberOfProducts() + " products loaded",
				emptyCatalog.getNumberOfProducts() == 0);
			
			// Testing a not empty file
			Catalog catalog =
				loader.loadCatalog("catalog.dat");
	
			assertTrue("3, testing method loadCatalog",
				catalog instanceof Catalog);
			assertTrue("4, testing method loadCatalog: "
				+ catalog.getNumberOfProducts() + " products loaded",
				catalog.getNumberOfProducts() == 26);
			
			// Testing product C001
			Product product = catalog.getProduct("C001");
			
			assertTrue("5, testing method loadCatalog" + product.toString(),
				product instanceof Coffee);
			
			Coffee coffeeC001 = (Coffee) product;
				
			assertTrue("6, testing method loadCatalog: " + 
				coffeeC001.toString(),
				coffeeC001.getCode().equals("C001") &&
				coffeeC001.getDescription().equals("Colombia, Whole, 1 lb") &&
				coffeeC001.getPrice() == 17.99 &&
				coffeeC001.getOrigin().equals("Colombia") &&
				coffeeC001.getRoast().equals("Medium") &&
				coffeeC001.getFlavor().equals("Rich and Hearty")  &&
				coffeeC001.getAroma().equals("Rich") &&
				coffeeC001.getAcidity().equals("Medium") &&
				coffeeC001.getBody().equals("Full"));
			
			// Testing product C002
			product = catalog.getProduct("C002");
			
			assertTrue("7, testing method loadCatalog: " + product.toString(),
				product instanceof Coffee);
			
			Coffee coffeeC002 = (Coffee) product;
				
			assertTrue("8, testing method loadCatalog: " + 
				coffeeC002.toString(),
				coffeeC002.getCode().equals("C002") &&
				coffeeC002.getDescription().equals("Colombia, Ground, 1 lb") &&
				coffeeC002.getPrice() == 18.75 &&
				coffeeC002.getOrigin().equals("Colombia") &&
				coffeeC002.getRoast().equals("Medium") &&
				coffeeC002.getFlavor().equals("Rich and Hearty")  &&
				coffeeC002.getAroma().equals("Rich") &&
				coffeeC002.getAcidity().equals("Medium") &&
				coffeeC002.getBody().equals("Full"));
				
			// Testing product A001
			product = catalog.getProduct("A001");
			
			assertTrue("9, testing method loadCatalog: " + product.toString(),
				product instanceof Product);
				
			assertTrue("10, testing method loadCatalog: " + 
				product.toString(),
				product.getCode().equals("A001") &&
				product.getDescription().equals("Almond Flavored Syrup") &&
				product.getPrice() == 9.0);
				
			// Testing product B002
			product = catalog.getProduct("B002");
			
			assertTrue("11, testing method loadCatalog: " + product.toString(),
				product instanceof CoffeeBrewer);
	
			CoffeeBrewer brewerB002 = (CoffeeBrewer) product;
			
			assertTrue("12, testing method loadCatalog: " + 
				brewerB002.toString(),
				brewerB002.getCode().equals("B002") &&
				brewerB002.getDescription().equals("Coffee Brewer, 2 Warmers") &&
				brewerB002.getPrice() == 200.0 &&
				brewerB002.getModel().equals("Brewer 200") &&
				brewerB002.getWaterSupply().equals("Pourover") &&
				brewerB002.getNumberOfCups() == 12);
				
		} catch (Exception e) {
			fail("13, testing method loadCatalog: " + e.getMessage());
		}
	}
}

GourmetCoffeeSystem类

import java.io.*;
import java.util.*;
import java.text.*;

/**
 * This class implements a gourmet coffee system.
 *
 * @author BlankSpace
 * @version 1.1.0
 * @see Product
 * @see Coffee
 * @see CoffeeBrewer
 * @see Catalog
 * @see OrderItem
 * @see Order
 * @see SalesFormatter
 * @see PlainTextSalesFormatter
 * @see HTMLSalesFormatter
 * @see XMLSalesFormatter
 * @see CatalogLoader
 * @see FileCatalogLoader
 */
public class GourmetCoffee  {

	private static BufferedReader  stdIn =
		new  BufferedReader(new  InputStreamReader(System.in));
	private static PrintWriter  stdOut = new  PrintWriter(System.out, true);
	private static PrintWriter  stdErr = new  PrintWriter(System.err, true);

	private Catalog  catalog;
	private Sales  sales;

	private SalesFormatter salesFormatter;

	/**
	 * Loads catalog data from a file and starts the application.
	 * <p>
	 * The name of the file is specified in the command arguments.
	 * </p>
	 *
	 * @param args  String arguments.
	 * @throws IOException if there are errors in the input.
	 */
	public static void  main(String[]  args) throws IOException  {

		Catalog catalog = null;

		if (args.length != 1) {
			stdErr.println("Usage: java GourmetCoffee filename");
		} else {
			try {
				catalog =
					(new FileCatalogLoader()).loadCatalog(args[0]);
			} catch (FileNotFoundException fnfe) {
				stdErr.println("The file does not exist");

				System.exit(1);

			} catch (DataFormatException dfe) {
				stdErr.println("The file contains malformed data: "
				               + dfe.getMessage());

				System.exit(1);
			}

			GourmetCoffee  application =
				new GourmetCoffee(catalog);

			application.run();
		}
	}

	/**
	 * Constructs a <code>GourmetCoffee</code> object.
	 * Initializes the catalog data with the value specified
	 * in the parameter.
	 *
	 * @param initialCatalog a product catalog
	 */
	private GourmetCoffee(Catalog initialCatalog) {

		this.catalog = initialCatalog;
		this.sales = new Sales();
		this.salesFormatter =
			PlainTextSalesFormatter.getSingletonInstance();

		loadSales();
	}

	/**
	 * Initializes the sales object.
	 */
	private void loadSales() {

		Order orderOne = new Order();
		Product productOne = this.catalog.getProduct("C001");
		
		if (productOne != null) {
			orderOne.addItem(new OrderItem(productOne, 5));
			this.sales.addOrder(orderOne);
		}

		Order orderTwo = new Order();
		Product productTwo = this.catalog.getProduct("C002");
		Product productThree = this.catalog.getProduct("A001");

		if ((productTwo != null) && (productThree != null)) {
			orderTwo.addItem(new OrderItem(productTwo, 2));
			orderTwo.addItem(new OrderItem(productThree, 2));
			this.sales.addOrder(orderTwo);
		}

		Order orderThree = new Order();
		Product productFour = this.catalog.getProduct("B002");

		if (productFour != null) {
			orderThree.addItem(new OrderItem(productFour, 1));
			this.sales.addOrder(orderThree);
		}
	}

	/**
	 * Presents the user with a menu of options and executes the
	 * selected task.
	 */
	private void run() throws IOException  {

		int  choice = getChoice();

		while (choice != 0)  {
			if (choice == 1)  {
				displayCatalog();
			} else if (choice == 2)  {
				this.salesFormatter =
					PlainTextSalesFormatter.getSingletonInstance();
				writeFile(
					readFilename(),
					this.salesFormatter.formatSales(this.sales));
			} else if (choice == 3)  {
				this.salesFormatter =
					HTMLSalesFormatter.getSingletonInstance();
				writeFile(
					readFilename(),
					this.salesFormatter.formatSales(this.sales));
			} else if (choice == 4)  {
				this.salesFormatter =
					XMLSalesFormatter.getSingletonInstance();
				writeFile(
					readFilename(),
					this.salesFormatter.formatSales(this.sales));
			}

			choice = getChoice();
		}
	}

	/**
	 * Displays a menu of options and verifies the user's choice.
	 *
	 * @return an integer in the range [0,7]
	 */
	private int  getChoice() throws IOException  {

		int  input;

		do  {
			try  {
				stdErr.println();
				stdErr.print("[0]  Quit\n"
				             + "[1]  Display Catalog\n"
				             + "[2]  Save sales (Plain Text)\n"
				             + "[3]  Save sales (HTML)\n"
				             + "[4]  Save sales (XML)\n"
				             + "choice> ");
				stdErr.flush();

				input = Integer.parseInt(stdIn.readLine());

				stdErr.println();

				if (0 <= input && 4 >= input)  {
					break;
				} else {
					stdErr.println("Invalid choice:  " + input);
				}
			} catch (NumberFormatException  nfe)  {
				stdErr.println(nfe);
			}
		}  while (true);

		return  input;
	}

	/**
	 * Displays the catalog.
	 */
	private void displayCatalog() {

		int size = this.catalog.getNumberOfProducts();

		if (size == 0) {
			stdErr.println("The catalog is empty");
		} else {
			for (Product product : this.catalog) {
				stdOut.println(product.getCode() + " "
				               + product.getDescription());
			}
		}
	}

	/**
	 * Creates a new file (which has the specified name) and writes
	 * the specified string to the new file.
	 *
	 * @param filename name of the file that will store the data
	 * @param content data to be stored
	 */
	private void writeFile(String filename, String content)
		throws IOException {

		/* PLACE YOUR CODE HERE */
        BufferedWriter buffer = new BufferedWriter(new FileWriter(filename));   
        buffer.write(content);   
        buffer.close();
	}

	/**
	 * Prompts the user for a filename (the name of the file that
	 * will store the sales information) and returns the user's
	 * response.
	 *
	 * @return name of a file
	 */
	private  String readFilename() throws IOException  {

		stdErr.print("Filename> ");
		stdErr.flush();

		return stdIn.readLine();
	}
}