package edu.columbia.cs.cs1007.roster;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;
import java.util.Comparator;
import java.util.InputMismatchException;
import java.util.Scanner;
import java.util.TreeSet;
/**
* This class reads in a student roster from a file, and sorts the
* entries by uni, major, or year. Sorted results are written
* to another file, and some simple statistics about the roster
* are displayed on the screen.
*
* @author Julia Stoyanovich (jds2109@columbia.edu)
* COMS 1007, Summer 2009
*
*/
public class Roster {
private static enum SORT_ORDER {UNI, MAJOR, YEAR}
// students are kept sorted on uni in the natural order
private TreeSet<Student> _students;
// input and output file handles
private FileReader _inFP;
private FileWriter _outFP;
// sorting options
private SORT_ORDER _sort;
private boolean _reverseSortOrder = false;
// maximum number of students to read from the file
private int _numStudents = Integer.MAX_VALUE;
/**
* Constructor.
*/
public Roster() {
_students = new TreeSet<Student>();
}
/**
* Parsing of command line arguments.
* @param args -i inFileName -o outFileName -s|r uni|major|year [-n numStudents]
* @throws FileNotFoundException
* @throws IOException
* @throws IllegalArgumentException
* @throws NumberFormatException
*/
public void parseArguments(String[] args) throws
FileNotFoundException,
IOException,
IllegalArgumentException,
NumberFormatException {
// arguments: -i inFileName -o outFileName -s|r uni|major|year [-n numStudents]
// we expect an even number of arguments: command followed by parameter
if (args.length %2 != 0) {
throw new IllegalArgumentException("Invalid number of arguments.");
}
for (int i=0; i<args.length; i+=2) {
// a command always has length 2: "-" followed by a letter
if (args[i].length() != 2) {
throw new IllegalArgumentException("Not a valid command option " + args[i]);
}
char command = args[i].charAt(1);
String param = args[i+1].trim();
switch (command) {
case 'i': // input file name
_inFP = new FileReader(param);
break;
case 'o': // output file name
_outFP = new FileWriter(param);
break;
case 's': // sort in the natural order
_sort = SORT_ORDER.valueOf(param.toUpperCase());
_reverseSortOrder = false;
break;
case 'r': // sort in reverse order
_sort = SORT_ORDER.valueOf(param.toUpperCase());
_reverseSortOrder = true;
break;
case 'n': // max number of students to read in
_numStudents = Integer.parseInt(param);
if (_numStudents <= 0) {
throw new IllegalArgumentException("Number of students must be positive.");
}
break;
default:
throw new IllegalArgumentException("Not a valid command option -" + command);
// note no "break;" here, since if we got here -- an exception is thrown,
// and the next line is unreachable.
}
}
// make sure that the required parameters are set
if (_inFP == null) {
throw new IllegalArgumentException("No input file specified.");
}
if (_outFP == null) {
throw new IllegalArgumentException("No output file specified.");
}
if (_sort == null) {
throw new IllegalArgumentException("No sort order specified.");
}
}
/**
* Return the usage string.
* @return Usage: Roster -i inFileName -o outFileName -s|r uni|major|year [-n numStudents]
*/
public String printUsage() {
// input and output files and sort order (either s or r) are required,
// number of students is optional
return "Usage: Roster -i inFileName -o outFileName -s|r uni|major|year [-n numStudents]";
}
/**
* Read and parse the input file, initializing the _students data structure.
*/
public void readInput() {
// open a scanner on the input file
Scanner scanner = new Scanner(_inFP);
int numProcessedLines=0;
// what is the current year?
Calendar rightNow = Calendar.getInstance();
int currentYear = rightNow.get(Calendar.YEAR);
while (scanner.hasNextLine() && (numProcessedLines < _numStudents)) {
String line = scanner.nextLine();
try {
// lines are expected to be of the form:uni,name,gradYear,major
String[] values = line.split(",");
if (values.length < 4) {
throw new InputMismatchException("Not enough values");
}
// parse the four fields
String name = values[0];
String uni = values[1];
int gradYear = Integer.parseInt(values[2].trim());
String major = values[3];
// note use of our exception type, IllegalGradYearException
if (gradYear < currentYear) {
throw new IllegalGradYearException();
}
// add the student to the TreeSet
_students.add(new Student(uni, name, gradYear, major));
numProcessedLines++;
} catch (InputMismatchException ime) {
System.out.println("Invalid line: " + line);
} catch (IllegalGradYearException igye) {
System.out.println(igye.getMessage() + "\t" + line);
} catch (NumberFormatException nfe) {
System.out.println(nfe.getMessage());
}
}
// close the open scanner
scanner.close();
}
/**
* Sort students on uni, major, or graduation year,
* in the requested sort order.
* @return a sorted Student[]
*/
public Student[] sort() {
Student[] sortedStudents;
if ((_sort == SORT_ORDER.UNI) && _reverseSortOrder) {
// sort on UNI in reverse order
sortedStudents = _students.descendingSet().toArray(new Student[0]);
} else {
// sort on UNI in natural order
sortedStudents = _students.toArray(new Student[0]);
}
if (_sort == SORT_ORDER.YEAR) {
// an inner class that implements a comparator on grad. year
class YearComparator implements Comparator<Student> {
public int compare(Student one, Student another) {
int cmp=0;
if (one.getGradYear() < another.getGradYear()) {
cmp = -1;
} else if (one.getGradYear() > another.getGradYear()) {
cmp = 1;
}
return cmp;
}
}
// instantiate the comparator
YearComparator comparator = new YearComparator();
// note that sorting of sortedStudents happens in place!
if (!_reverseSortOrder) {
// sort on year in the natural order
Arrays.sort(sortedStudents, comparator);
} else {
// sort on year in the reverse order
Arrays.sort(sortedStudents, Collections.reverseOrder(comparator));
}
} else if (_sort == SORT_ORDER.MAJOR) {
// an inner class that implements a comparator on major
class MajorComparator implements Comparator<Student> {
public int compare(Student one, Student another) {
return one.getMajor().compareTo(another.getMajor());
}
}
// instantiate the comparator
MajorComparator comparator = new MajorComparator();
if (!_reverseSortOrder) {
// sort in the natural order on major
Arrays.sort(sortedStudents, comparator);
} else {
// sort in reverse order on major
Arrays.sort(sortedStudents, Collections.reverseOrder(comparator));
}
}
return sortedStudents;
}
/**
* Return some simple statistics, as a string.
* @return number of student that were processed
*/
public String printStatistics() {
return "There are " + _students.size() + " students";
}
/**
* Output student data to an output file.
* @param students
* @throws IOException
*/
public void writeToFile(Student[] students) throws IOException {
int i=0;
for (Student student : students) {
if (i>0) {
_outFP.write("\n");
}
i++;
_outFP.write(student.toString());
}
}
/**
* Close any open file pointers.
*/
public void closeFilePointers() {
try {
if (_inFP != null) {
_inFP.close();
}
} catch (IOException ioe) {
// Note that the exception is suppressed.
// Acceptable only in the finally{} block.
}
try {
if (_outFP != null) {
_outFP.close();
}
} catch (IOException ioe) {}
}
public static void main(String[] args) {
Roster roster = new Roster();
try {
roster.parseArguments(args);
roster.readInput();
System.out.println(roster.printStatistics());
Student[] sortedStudents = roster.sort();
roster.writeToFile(sortedStudents);
roster.closeFilePointers();
} catch (NumberFormatException nfe) {
System.out.println("Invalid number: " + nfe.getMessage());
System.out.println(roster.printUsage());
} catch (IllegalArgumentException iae) {
System.out.println("You invoked the program in an illegal manner: " + iae.getMessage());
System.out.println(roster.printUsage());
} catch (FileNotFoundException fnfe) {
System.out.println("File not found: " + fnfe.getMessage());
System.out.println(roster.printUsage());
} catch (IOException ioe) {
System.out.println("IO Exception: " + ioe.getMessage());
System.out.println(roster.printUsage());
} finally {
roster.closeFilePointers();
}
}
}
|