package dk.silverbullet.telemed.device.accuchek; import dk.silverbullet.telemed.device.accuchek.BloodSugarMeasurement; import dk.silverbullet.telemed.device.accuchek.BloodSugarMeasurements; import java.io.*; import java.nio.charset.Charset; import java.text.DateFormat; import java.text.ParseException; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; public class CsvFileReader { private static final String FIRST_HEADER_LINE = "\uFEFFSerienummer;Overføringsdato;Overføringstidspunkt;;;;;;;"; private static final String START_OF_SECOND_HEADER_LINE_1 = "Dato;Tid;Måleresultat;Måleenhed;Temperaturadvarsel;Uden for målområde;Øvrigt;Før måltid;Efter måltid;Kontrolmåling"; private static final String START_OF_SECOND_HEADER_LINE_2 = "Dato;Klokkeslæt;Måleresultat;Måleenhed;Temperaturadvarsel;Uden for målområde;Øvrigt;Før måltid;Efter måltid;Kontrolmåling"; // SimpleDateFormat is not thread-safe, so we need to guard it with a ThreadLocal private static final ThreadLocal<DateFormat> DATE_FORMAT = new ThreadLocal<DateFormat>() { protected DateFormat initialValue() { return new SimpleDateFormat("dd.MM.yyyy HH:mm"); } }; public static BloodSugarMeasurements readFile(File file) throws IOException { List<String> lines = readFileAsLines(file); checkNumberOfLines(lines); BloodSugarMeasurements result = new BloodSugarMeasurements(); readMetadata(result, lines); readMeasurements(result, lines); return result; } private static void readMetadata(BloodSugarMeasurements measurements, List<String> lines) throws IOException { checkMetadataHeadline(lines); String[] components = lines.get(1).split(";"); checkNumberOfMetadataColumns(components); measurements.serialNumber = components[0]; measurements.transferTime = parseDate(components[1], components[2]); } private static void readMeasurements(BloodSugarMeasurements measurements, List<String> lines) throws IOException { checkMeasurementsHeadline(lines); for (int i = 3; i < lines.size(); i++) { String line = lines.get(i); if (line.trim().isEmpty()) { continue; } measurements.measurements.add(parseLine(line)); } } private static BloodSugarMeasurement parseLine(String line) throws IOException { String[] components = line.split(";"); checkNumberOfMeasurementColumns(components); BloodSugarMeasurement result = new BloodSugarMeasurement(); result.timeOfMeasurement = parseDate(components[0], components[1]); result.result = parseDouble(components[2]); checkUnits(components[3]); result.hasTemperatureWarning = parseCheckmark(components[4]); result.isOutOfBounds = parseCheckmark(components[5]); result.otherInformation = parseCheckmark(components[6]); result.isBeforeMeal = parseCheckmark(components[7]); result.isAfterMeal = parseCheckmark(components[8]); result.isControlMeasurement = parseCheckmark(components[9]); return result; } private static double parseDouble(String d) throws IOException { try { if(d.equalsIgnoreCase("HI")) { return 33.3; } if(d.equalsIgnoreCase("LO")) { return 0.6; } return Double.parseDouble(d); } catch (NumberFormatException e) { throw new CsvFormatException("Could not parse double: '" + d + "'", e); } } private static Date parseDate(String date, String timeOfDay) throws IOException { try { return DATE_FORMAT.get().parse(date + " " + timeOfDay); } catch (ParseException exception) { throw new CsvFormatException("Could not parse date '" + date + "' and time of day '" + timeOfDay + "'", exception); } } private static boolean parseCheckmark(String s) { return s.equals("X"); } private static void checkNumberOfLines(List<String> lines) throws IOException { if (lines.size() < 3) { throw new CsvFormatException("Too few lines in file: " + lines.size()); } } private static void checkMetadataHeadline(List<String> lines) throws IOException { String firstHeadline = lines.get(0); if (!firstHeadline.equals(FIRST_HEADER_LINE)) { throw new CsvFormatException("Wrong first headline: '" + firstHeadline + "'"); } } private static void checkMeasurementsHeadline(List<String> lines) throws IOException { String secondHeadline = lines.get(2); if (!secondHeadline.startsWith(START_OF_SECOND_HEADER_LINE_1) && !secondHeadline.startsWith(START_OF_SECOND_HEADER_LINE_2)) { throw new CsvFormatException("Wrong second headline: '" + secondHeadline + "'"); } } private static void checkNumberOfMetadataColumns(String[] columns) throws IOException { if (columns.length != 3) { throw new CsvFormatException("Wrong number of metadata columns: " + columns.length); } } private static void checkNumberOfMeasurementColumns(String[] columns) throws IOException { if (columns.length != 10) { throw new CsvFormatException("Wrong number of measurement columns: " + columns.length); } } private static void checkUnits(String units) throws IOException { if (!units.equals("mmol/l")) { throw new CsvFormatException("Wrong units: '" + units + "'"); } } private static List<String> readFileAsLines(File file) throws IOException { InputStreamReader reader = new InputStreamReader(new FileInputStream(file), Charset.forName("utf-8")); try { BufferedReader bufferedReader = new BufferedReader(reader); try { List<String> result = new ArrayList<String>(); for (String line = bufferedReader.readLine(); line != null; line = bufferedReader.readLine()) { result.add(line); } return result; } finally { bufferedReader.close(); } } finally { reader.close(); } } }