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