// Class ObstCourseDataFileHandler // // Author: Alyce Brady // // This class is based on the College Board's MBSDataFileHandler class, // as allowed by the GNU General Public License. MBSDataFileHandler // is a black-box class within the AP(r) CS Marine Biology Simulation // case study (see www.collegeboard.com/ap/students/compsci). // // License Information: // This class is free software; you can redistribute it and/or modify // it under the terms of the GNU General Public License as published by // the Free Software Foundation. // // This class is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU General Public License for more details. import java.io.File; import java.io.FileReader; import java.io.LineNumberReader; import java.io.FileWriter; import java.io.PrintWriter; import java.util.StringTokenizer; import edu.kzoo.grid.Grid; import edu.kzoo.grid.GridObject; import edu.kzoo.grid.Location; import edu.kzoo.grid.gui.GridDataFileHandler; /** * Obstacle Course Program:
* * A ObstCourseDataFileHandler object reads and writes * information about an Obstacle Course grid to and from a data * file, including the grid's dimensions and the locations of * the obstacles. * file, including the grid's dimensions, the number of racers, * and the locations of the obstacles. * *

* The first line of the input file should contain the dimensions of * the obstacle course. Subsequent lines should specify the locations * of the obstacles. The file format is: * obstacle course and the number of racers. Subsequent lines should * specify the locations of the obstacles. The file format is: *

 *     rows columns
 *     rows columns numRacers
 *     row-pos col-pos
 *     row-pos col-pos
 *     ...
 *     row-pos col-pos
 *  
* where rows and columns are integers * indicating the number of rows and columns in the grid, * numRacers is an integer indicating the number of racers, * and row-pos and col-pos are integers * indicating the row and column position of an obstacle. * * @author Alyce Brady (based on MBSDataFileHandler) * @version 29 February 2004 **/ public class ObstCourseDataFileHandler implements GridDataFileHandler { // Encapsulated data used to read/write obstacle course info from a file private LineNumberReader inputReader; // buffered input w/ line number private StringTokenizer tokenizer; // parses tokens from a line /** Reads information about an obstacle course from an initial configuration * data file. * @param file java.io.File object from which to read * @throws java.io.FileNotFoundException if file cannot be opened * @throws RuntimeException if invalid information is read from file **/ public Grid readGrid(File file) throws java.io.FileNotFoundException { // Open the file for reading. inputReader = new LineNumberReader(new FileReader(file)); // Read obstacle course dimensions and construct it. Grid grid = buildGrid(); // Read the number of racers. // int numRacers = getNumRacers(grid); // Read obstacle locations. while ( readObstacle(grid) == true ) ; // Place the racers. /* for ( int r = 0; r < numRacers; r++ ) if ( ! grid.placeRacer() ) throw new RuntimeException("Error placing racers - not enough columns " + "(line " + inputReader.getLineNumber() + ")"); */ return grid; } /** Read obstacle course dimensions and construct it. * @throws RuntimeException if invalid grid information is read **/ private ObstacleCourse buildGrid() { int gridRows, gridCols; try { // Read the grid dimensions. gridRows = readInt(); gridCols = readInt(); } catch (Exception e) { throw new RuntimeException("Error reading grid dimensions " + "(line " + inputReader.getLineNumber() + ")"); } // Validate grid dimensions. if ( gridRows <= 0 || gridCols <= 0 ) throw new RuntimeException("Grid dimensions must be positive (line " + inputReader.getLineNumber() + ")"); // Construct the appropriate bounded grid. ObstacleCourse newGrid = new ObstacleCourse(gridRows, gridCols); return newGrid; } /** Reads the number of racers to place in the grid. * @param grid the grid * @throws RuntimeException if invalid grid information is read **/ /* private int getNumRacers(Grid grid) { int numRacers; try { // Read the number of racers. numRacers = readInt(); } catch (Exception e) { throw new RuntimeException("Error reading the number of racers " + "(line " + inputReader.getLineNumber() + ")"); } // Validate the number of racers. if ( numRacers < 0 || numRacers > grid.numCols() ) throw new RuntimeException("Number of racers must be positive but not " + "greater than number of columns (line " + inputReader.getLineNumber() + ")"); return numRacers; } /** Reads location information for the next obstacle and constructs it. * The obstacle adds itself to the grid as it is constructed. * @param grid the grid in which obstacle should be created * @return true if obstacle was successfully read, * false at EOF * @throws RuntimeExceptions if invalid location information is read **/ private boolean readObstacle(Grid grid) { // Read an obstacle's location (if there is another one in the file). Location loc = readLocation(grid); if ( loc == null ) return false; // Construct the obstacle in the grid. new Obstacle(grid, loc); return true; } /** Reads in a Location object (must be a valid location in * grid). * @param grid grid in which location must be valid * @return the newly created Location object * @throws MBSException if invalid location information is read **/ private Location readLocation(Grid grid) { int row, col; try { // Read the location. row = readInt(); col = readInt(); } catch (java.io.EOFException e) { // Reached end of file return null; } catch (Exception e) { // Convert reading exceptions to MBSException. throw new RuntimeException("Error reading location (line " + inputReader.getLineNumber() + ")"); } Location loc = new Location(row, col); // Verify that location is valid in this grid. if ( grid.isValid(loc) ) return loc; else throw new RuntimeException("Location " + loc + " is not valid in this grid (line " + inputReader.getLineNumber() + ")"); } /** Returns the next token in the file as an integer. * @return an int containing the next number in the file * @throws java.io.EOFException if EOF * @throws java.lang.NumberFormatException if next token is not an int * @throws java.io.IOException if another type of input error occurs **/ private int readInt() throws java.io.IOException { // Read in number as string, then convert to integer. String token = readString(); if ( token == null ) throw new java.io.EOFException(); return Integer.parseInt(token); } /** Returns the next token in the file as a string. * @return a String containing the next token in the file; or null * if end of file is encountered * @throws java.io.IOException if an input error occurs **/ private String readString() throws java.io.IOException { // Read in a new line if there are no more tokens in current line. while ( tokenizer == null || ! tokenizer.hasMoreTokens() ) { String line = inputReader.readLine(); // Did we encounter end of file? if ( line == null ) return null; tokenizer = new StringTokenizer(line); } // Return next token. return tokenizer.nextToken(); } /** Writes information about a grid into a data file, * including the grid dimensions and the locations of * the obstacles. * @param grid grid to write to file * @param file java.io.File object to which to write * @throws java.io.FileNotFoundException if file cannot be opened * @throws java.io.IOException if error writing to file **/ public void writeGrid(Grid grid, File file) throws java.io.IOException { PrintWriter out = new PrintWriter(new FileWriter(file)); // Save grid dimensions. out.println(grid.numRows() + " " + grid.numCols()); // Save the number of racers. // out.println(grid.numRows() + " " + grid.numCols()); // Save the locations of all the obstacles. GridObject[] objList = grid.allObjects(); for ( int index = 0; index < objList.length; index++ ) { if ( objList[index] instanceof Obstacle ) { int row = objList[index].location().row(); int col = objList[index].location().col(); out.println(row + " " + col); } } out.close(); } }