The complete specification of assignment #6 can be found as part of the stream at iTunes.
Implement the NameSurferEntry class
The NameSurferEntry class encapsulates the information pertaining to one name in the database. That information consists of two parts:
- The name itself, such as “Sam” or “Samantha”
- A list of 11 values indicating the rank of that name in each of the decades from 1900
to 2000, inclusiveThe class definition begins with a constructor that creates an entry from the line of data
that appears in the NamesData.txt file. For example, the entry for Sam looks like this:Sam 58 69 99 131 168 236 278 380 467 408 466The idea behind the design of this constructor is that it should be possible to read a line of data from the file and then create a new entry for it using code that looks like this:
String line = rd.readLine(); NameSurferEntry entry = new NameSurferEntry(line);
The implementation of the constructor has to divide up the line at the spaces, convert the digit strings to integers (using Integer.parseInt), and then store all of this information as the private state of the object in such a way that it is easy for the getName and getRank methods to return the appropriate values.
The last method in the starter implementation of NameSurferEntry is a toString method whose role is to return a human-readable representation of the data stored in the entry. For example, if the variable entry contains the NameSurferEntry data for Sam, you might want entry.toString() to return a string like this:"Sam [58 69 99 131 168 236 278 380 467 408 466]"Defining toString for a class has the wonderful advantage that it makes it possible to print out objects of that class using println, just as you do for primitive values. Whenever Java needs to convert an object to a string, it always calls its toString method to do the job. The default definition of toString in the Object class doesn’t supply much useful information, and you will find that your debugging sessions get much easier if you can look easily at the values of your objects.
To show that you’ve got NameSurferEntry implemented correctly, you might want to write a very simple test program that creates an entry from a specific string and then verifies that the other methods work as they are supposed to.
An entry needs to hold a name and an the ranks. Thus create a string instance for the name and TreeMap instance – which is a sorted HashMap – to hold the ranks with the decades as keys:
private String name; private TreeMap<Integer,Integer> ranks = new TreeMap<Integer,Integer>();
The constructor uses a scanner to pars the provided line from the text file. First it separates the line using a space as delimiter. The first value should be the name, if there is not any there is no need to continue. Every further string should be a rank value which is filled into the TreeMap using the current decade as key:
public NameSurferEntry(String line) { Scanner scanner = new Scanner(line); scanner.useDelimiter(NAMESTRING_DELIMITER); if (!scanner.hasNext()) { name = "error"; return; } name = scanner.next(); int year = START_DECADE; while (scanner.hasNext()) { ranks.put(year, Integer.parseInt(scanner.next())); year += DECADE; } }
Returning the name means just to read out the local name instance:
public String getName() { return name; }
To return a rank it for the nth decade, it is first necessary calculate the key. If that specific year does not exist, return zero otherwise the rank value:
public int getRank(int decade) { Integer rank = ranks.get(START_DECADE + DECADE * decade); if (rank == null) return 0; return rank; }
To create the string add first the name and the opening bracket, loop and add all available ranks, and finally close the brackets:
public String toString() { String result = name + TOSTRING_DELIMITER + TOSTRING_START_RANK; String next = ""; Iterator<Integer> it = ranks.keySet().iterator(); while (it.hasNext()) { result += next + ranks.get(it.next()); next = TOSTRING_DELIMITER; } result += TOSTRING_STOP_RANK; return result; }
… and use constants heavily:
private static final String NAMESTRING_DELIMITER = " "; private static final int DECADE = 10; private static final String TOSTRING_START_RANK = "["; private static final String TOSTRING_STOP_RANK = "]"; private static final String TOSTRING_DELIMITER = " ";
For initial testing, use the text field, e.g.:
public void actionPerformed(ActionEvent e) { Object source = e.getSource(); if ((source == nameField) || (source == graphButton)) { //println("Graph: \"" + nameField.getText() + "\""); NameSurferEntry entry = new NameSurferEntry(nameField.getText()); println(entry.getName()); println(entry.getRank(0)); println(entry.getRank(NDECADES - 2)); println(entry.getRank(NDECADES)); println(entry.toString()); } else if (source == clearButton) { println("Clear"); } }
The code for this assignment is available on github.