For this assignment, you will write a helper class that will be used by a JavaFX program to display the popularity of a baby name over the last 115 years (based on the Social Security births). The JavaFX GUI file is provided as part of the assignment. It uses a class call NameHelper (which you will write) to load the data files and to fetch name rankings by year. Make sure your data helper follows the follow UML:
NameHelper |
~girlssByYear: HashMap< String, Map< String, Integer > > ~boysByYear: HashMap< String, Map< String, Integer > > |
+isNamePresent(): boolean +getYears(): Set< String> +getRank(): int +load(): void throws FileNotFoundException |
This assignment will utilize the following concepts:
We will be using the data files from the Social Security Administration. (https://www.ssa.gov/oact/babynames/limits.html) A zip of these files is included with the assignment, or you can download them from the provided link. Note: we will only use the data files from 1900 onwards.
There is a readme file included with the data that describes the format. Make sure you read it as you'll need to know the file structure to parse the data.
Place the data files from 1900 onward into a package named data. This package should be at the root of your project.
You will be creating a data structure that will be used to store the data from the files. This data structure will be a map of maps. We'll use maps since searches are easier and faster with maps. The outer map's keys will be the years. The value associated with a key will be a map whose keys are the names, and values are the rankings. We'll utilize two of these data structures, one for each gender. A visualization of the data structure is shown below for the year 1900 and 1901. Only some names are shown in the inner map. see image.
This class will utilize two data structures: boysByYear and girlsByYear. They will be a Map of Maps. The data types are shown in the UML. The keys for the outer map are strings, representing the years. The keys for the inner map are Strings, representing the names. The ranking will be of type Integer. Note that the visibility modifier for these two maps are package level, not private.
Load()
The load() method will read all of the data files in the src/data package.
File dir = new File("src/data");
File [] files = dir.listFiles();
// for each file in the directory...
for (File f : files)
{
/* TODO:
while the file f is not empty
read a line get the name, gender, and ranking
add the data to the maps
*/
}
This code above will read all of the data files and process them one by one. You have to write code inside the for loop to open the file and read a line. There are several ways you can process the line -- but utilizing the split() method in the String class is probably the easiest. You need to build both data structures (boysByYear and girlsByYear) as you process the file.
getYears()
This method should return a sorted Set of years. The easiest way to get the years is to get the keyset from either boysByYear or girlsByYear. Use a TreeSet to represent the set. It's important to have a sorted set of years, as this is how it will appear in the viewer.
isNamePresent()
This method will return true if the supplied name is found in the appropriate data structure. Use the gender input to determine which map to search. There's a method on maps named "containsKey" that you can use.
getRank()
This method will return the ranking for a given name and gender. Use the gender input to determine which map to search and return the value associated with the name key.
Things to remember
The user is allowed to enter Upper or Lower case names in the viewer, so make sure you account for case when searching your maps for a name.
A name may not be present in all data files. If a name is not present, the the getRank() should return 0 for the ranking.
This JavaFX file shows some of the capabilities that JavaFX has to offer for charting data. Take a look and see how easy it is to create a chart. You can see some other examples here: http://docs.oracle.com/javafx/2/charts/jfxpub-charts.htm
Put this class in the same package as your solution. (You'll have to change the package name). Once it's running, select the gender, then type in a name and hit enter. The graph will be populated with data associated with that name.
Here's the results for "Noah". It's been the most popular boy name for the last few years (according to the SSA). You can see the surge in popularity in our chart. see image.
Baby Name Chart code (this was provided)
import java.io.FileNotFoundException;
import javafx.collections.ObservableList;
import javafx.application.Application;
import javafx.collections.FXCollections;
import javafx.event.ActionEvent;
import javafx.event.EventHandler;
import javafx.geometry.Insets;
import javafx.geometry.Pos;
import javafx.scene.Scene;
import javafx.scene.chart.AreaChart;
import javafx.scene.chart.CategoryAxis;
import javafx.scene.chart.LineChart;
import javafx.scene.chart.NumberAxis;
import javafx.scene.chart.XYChart;
import javafx.scene.control.Alert;
import javafx.scene.control.Alert.AlertType;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.RadioButton;
import javafx.scene.control.TextField;
import javafx.scene.control.ToggleGroup;
import javafx.scene.layout.HBox;
import javafx.scene.layout.VBox;
import javafx.stage.Stage;
public class BabyNameChart extends Application
{
XYChart.Series series = new XYChart.Series();
// our helper class to fetch needed data
NameHelper h = new NameHelper();
/*
* This method will draw the graph of name popularity vs year
*/
private void draw(String name, String gender)
{
series.getData().clear();
// first check if name is present
if (h.isNamePresent(name, gender) == false)
{
Alert alert = new Alert(AlertType.ERROR);
alert.setTitle("Error Dialog");
alert.setHeaderText("ERROR");
alert.setContentText(name + " not found!");
alert.showAndWait();
return;
}
for(String year : h.getYears())
{
Integer rank = h.getRank(year, name, gender);
// add a data point (year, rank) to our series.
series.getData().add( new XYChart.Data(year,rank) );
// for debug...
//System.out.printf("%4s-%4d\n", year, h.getRank(year, name, gender));
}
}
public void start(Stage stage) throws FileNotFoundException
{
h.load();
stage.setTitle("Baby Name Popularity");
final CategoryAxis xAxis = new CategoryAxis(); // for string values on x axis
final NumberAxis yAxis = new NumberAxis(); // for integer values on y axis
// label our axis
xAxis.setLabel("Year");
yAxis.setLabel("Popularity");
yAxis.setAutoRanging(true);
//creating the chart. Data will be added later.
// LineChart< String,Number> lineChart = new LineChart< String,Number>(xAxis,yAxis);
AreaChart< String,Number> chart = new AreaChart< String,Number>(xAxis,yAxis);
chart.setTitle("Popularity by Year");
chart.getData().addAll(series);
// setting animated off fixes an apparent bug with charts that don't have symbols
// If animated = on and symbols = off, then it doesn't always redraw correctly at
// the ends of the data range.
chart.setAnimated(false);
chart.setCreateSymbols(false);
//-------------------------
// text field for name
//-------------------------
HBox paneForText = new HBox(5);
Label lb1 = new Label("Name:");
TextField tfMessage = new TextField();
paneForText.setAlignment(Pos.CENTER);
paneForText.getChildren().addAll(lb1,tfMessage);
//-------------------------
// radio buttons for gender
//-------------------------
HBox panelForGender = new HBox(10);
RadioButton male = new RadioButton("Male");
RadioButton female = new RadioButton("Female");
male.setSelected(true);
panelForGender.setAlignment(Pos.CENTER);
panelForGender.getChildren().addAll(male,female);
// add to group
ToggleGroup group = new ToggleGroup();
male.setToggleGroup(group);
female.setToggleGroup(group);
//-------------------------
// button panel
//-------------------------
HBox paneForButtons = new HBox(5);
Button goButton = new Button();
goButton.setText("Go!");
tfMessage.setOnAction(new EventHandler< ActionEvent>()
{
@Override
public void handle(ActionEvent event)
{
String gender = "M";
if (female.isSelected())
gender = "F";
String name = tfMessage.getText();
// add label to series at bottom of chart
series.setName(name);
// get the data set for the name
draw(name,gender);
}
});
paneForButtons.setAlignment(Pos.CENTER);
paneForButtons.getChildren().addAll(goButton);
//-------------------------------------------
// H pane to hold all three previous panes
//-------------------------------------------
HBox topPane = new HBox(50);
topPane.setPadding(new Insets(15, 5, 15, 15));
topPane.setAlignment(Pos.CENTER);
topPane.getChildren().addAll(paneForText,panelForGender);//,paneForButtons);
//------------------------------------------------------------
// vertical pane will hold the horizontal composite pane and the chart
//-------------------------------------------------------------
VBox vPane = new VBox(10);
vPane.setPadding(new Insets(5, 5, 15, 15));
vPane.getChildren().add(topPane);
vPane.getChildren().add(chart);
//------------------------------------------------
// build and show scene
//------------------------------------------------
Scene scene = new Scene(vPane,800,500);
stage.setScene(scene);
stage.show();
}
public static void main(String[] args)
{
launch(args);
}
}