package edu.upenn.cit594.datamanagement; import java.io.BufferedReader; import java.io.File; import java.io.FileNotFoundException; import java.io.FileReader; import java.io.IOException; import java.util.ArrayList; import java.util.HashMap; import edu.upenn.cit594.ui.UserInterface; import edu.upenn.cit594.util.Property; public class PropertiesReader { protected static String filename; public PropertiesReader(String name) { filename = name; } /** * * @param filename * @return */ public ArrayList readPropertiesCSV (){ File csvFile = new File(filename); if(!csvFile.exists()) { UserInterface.print("error: Properties csv file does not exist."); return null; } else if (!csvFile.canRead()) { UserInterface.print("error: Propertie csv file can not be read."); return null; } //To store the Property Object Entries ArrayList Properties = new ArrayList(); //Stores BufferedReader line String line = ""; try { try (BufferedReader br = new BufferedReader(new FileReader(csvFile))) { //Get Key:Val pair of columns Name:Index HashMap headerColsMap = new HashMap(); headerColsMap = readHeader(filename); int numColumns = headerColsMap.size(); //Initialize column indexes int zipCodeIndex = -1; int livableAreaIndex = -1; int marketValueIndex= -1; //Reads the first line to get header row out of the way @SuppressWarnings("unused") String headerLine = br.readLine(); //Loop through the rest of csv file while((line = br.readLine()) != null) { //Stores column values String[] colValues = new String[numColumns]; int startIndex = 0; //start index for character int endIndex = 0; //end index for character int currentIndex = 0; //column index //Character Reader //Splits by "," but ignores any commas surrounded by double quotes for (int i=0; i < line.length(); i++) { //cast character to string String c = Character.toString(line.charAt(i)); boolean inQuotes = false; //Any time you see quotes, quotes boolean get's toggled if (c.equals("\"")) { inQuotes = !inQuotes; //if we get to the end of the line, then parse last column out } else if (i == line.length()-1) { //if the last character is a comma if (c.equals(",")) { //store value as an empty string colValues[colValues.length-1] = ""; } else { //store value as whatever the string is colValues[colValues.length-1] = line.substring(startIndex); } //if we see a comma and not in quotes in the middle of the line }else if ((c.equals(",") && inQuotes == false)) { //index i will be the comma endIndex = i; //if index is at end of the column if (currentIndex >= numColumns) { break; } //add the column value colValues[currentIndex] = line.substring(startIndex, endIndex); currentIndex++; // new start index to next character index startIndex = endIndex+1; endIndex = startIndex; } } //Check column name exists for Object Fields boolean zipCodeExists = headerColsMap.containsKey("zip_code"); boolean livableAreaExists = headerColsMap.containsKey("total_livable_area"); boolean marketValueExists = headerColsMap.containsKey("market_value"); //Set column indexes //if any of the necessary columns are missing from the csv file, display warning message. if (zipCodeExists) { zipCodeIndex = headerColsMap.get("zip_code"); }else { UserInterface.print("Warning: csv file is missing a \"zip_code\" column."); } if (livableAreaExists) { livableAreaIndex = headerColsMap.get("total_livable_area"); }else { UserInterface.print("Warning: csv file is missing a \"total_livable_area\" column."); } if (marketValueExists) { marketValueIndex = headerColsMap.get("market_value"); }else { UserInterface.print("Warning: csv file is missing a \"market_value\" column."); } //If the zipcode is missing or non-numeric, store a "-1" if(colValues[zipCodeIndex] == null || colValues[zipCodeIndex].equals("") || !isNumeric(colValues[zipCodeIndex])) { colValues[zipCodeIndex] = "-1"; } //If the total livable area is missing or non-numeric, store a "-1" if(colValues[livableAreaIndex] == null || colValues[livableAreaIndex].equals("") || !isNumeric(colValues[livableAreaIndex])) { colValues[livableAreaIndex] = "-1"; } //If the market value is missing or non-numeric, store a "-1" //NOTE: REMINDER THAT WHEN WE DO MATH TO IGNORE ANY VALUES WHERE if(colValues[marketValueIndex] == null || colValues[marketValueIndex].equals("") || !isNumeric(colValues[marketValueIndex])) { colValues[marketValueIndex] = "-1"; } //If the ZIP Code has fewer than 5 characters or //the first 5 characters are not all numeric, then skip to next data row if(colValues[zipCodeIndex].strip().length() < 5 || !isNumeric(colValues[zipCodeIndex].substring(0, 5))) { continue; } //Add a new CovidData set Properties.add(new Property( Integer.parseInt(colValues[zipCodeIndex].substring(0, 5)), // set zip code (only first 5 digits) Double.parseDouble(colValues[livableAreaIndex]), // set total livable area Double.parseDouble(colValues[marketValueIndex]) // set market value )); } br.close(); return Properties; } catch (NumberFormatException e) { e.printStackTrace(); } } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } //If we don't make it into the try, then returns empty ArrayList return Properties; } /** * Get the column index of every column's name * @param filename: the csv file * @return a HashMap where the key is the column's name and the value is it's index */ public static HashMap readHeader (String filename){ File csvFile = new File(filename); if(!csvFile.exists()) { UserInterface.print("error: csv file does not exist."); return null; } else if (!csvFile.canRead()) { UserInterface.print("error: csv file can not be read."); return null; } //Keys: column's name //Values: column's index HashMap headerColsMap = new HashMap(); try { try (BufferedReader br = new BufferedReader(new FileReader(csvFile))) { String headerLiner = br.readLine(); //Splits by "," but ignores any commas surrounded by double quotes String[] headerArray = headerLiner.split(",(?=(?:[^\\\"]*\\\"[^\\\"]*\\\")*[^\\\"]*$)"); //Stores index of column name int i = 0; for(String col : headerArray) { headerColsMap.put(col, i); i++; } br.close(); } return headerColsMap; } catch (FileNotFoundException e) { e.printStackTrace(); } catch (IOException e) { e.printStackTrace(); } return headerColsMap; } /** * checks if a string is a number * @param string input * @return true is string is numeric, else false */ public static boolean isNumeric(String str) { return str.matches("^-?[0-9]\\d*(\\.\\d+)?$"); //match a number with optional decimal ad negative. } }