### Question:
CsvReader { public void readCsv(InputStream inputStream, CsvReaderHandler csvHandler) throws IOException { List<String> lines = IoUtils.readToStringList(inputStream); inputStream.close(); if (lines.size() < 2) { return; } String titleLine = lines.get(0); StringTokenizer titleTokenizer = StringTokenizer.getCSVInstance(titleLine); List<String> titles = new LinkedList<>(); while (titleTokenizer.hasNext()) { titles.add(titleTokenizer.nextToken()); } csvHandler.onTitle(titles); for (int i = 1; i < lines.size(); i++) { String line = lines.get(i); StringTokenizer valueTokenizer = StringTokenizer.getCSVInstance(line); List<String> values = new LinkedList<>(); while (valueTokenizer.hasNext()) { values.add(valueTokenizer.nextToken()); } if (values.size() != titles.size()) { throw new RuntimeException(String.format("Row %d has different numbers of columns(%d) to titles(%d)", i, values.size(), titles.size())); } csvHandler.onRow(i, values); for (int j = 0; j < values.size(); j++) { csvHandler.onCell(j, titles.get(j), values.get(j)); } csvHandler.onRowFinished(i); } }  void readCsv(InputStream inputStream, CsvReaderHandler csvHandler);  }### Answer:
@Test public void test() throws IOException { System.out.println("Parse CSV: \n" + CSV_DATA.toString()); CsvReader visitor= new CsvReader(); visitor.readCsv(new ByteArrayInputStream(CSV_DATA.toString().getBytes()), new CsvReaderHandler() { @Override public void onTitle(List<String> titles) { System.out.println("Title: " + StringUtils.join(titles, " | ")); } @Override public void onRow(int rowNum, List<String> columns) { System.out.println("Row: " + StringUtils.join(columns, " | ")); } @Override public void onCell(int colNum, String columnName, String cellValue) { } @Override public void onRowFinished(int rowNow) { System.out.println("Ends with row: " +rowNow); } }); System.out.println("----"); }