There was a suggestion in this StackOverflow question to move this question here. I added some more information and restructured the question a bit.
I'm trying to parse the following file which contains information in the following format:
TABLE_NAME
VARIABLE_LIST_OF_COLUMNS
VARIABLE_NUMBER_OF_ROWS (Seperated by a tab seperator)
An example (using ',' as the seperator for the question; actual seperator is a tab):
STUDENTS
ID
NAME
1,Mike
2,Kimberly
The idea is to build a list of insert sql statements (context for the code snippet). So the output would be (ignore numeric/null values for now):
INSERT INTO STUDENTS (ID, NAME) VALUES ('1','Mike');
INSERT INTO STUDENTS (ID, NAME) VALUES ('2','Kimberly');
What I want to know is whether this kind of multiline parsing is at all possible using java 8 streams API? This is what I have at the moment:
public final class StatementGeneratorMain {
public static void main(final String[] args) throws Exception{
List<String> fileNames = Arrays
.asList("STUDENTS.txt");
fileNames.stream()
.forEach(fileName -> {
String tableName;
List<String> columnNames;
List<String[]> dataRows;
try (BufferedReader br = getBufferedReader(fileName)) {
tableName = br.lines().findFirst().get();
} catch (Exception e) {
throw new RuntimeException(e);
}
try (BufferedReader br = getBufferedReader(fileName)) {
//skip the first line because its been processed.
columnNames = br.lines().skip(1).filter(v -> v.split("\t").length == 1).collect(toList());
} catch (Exception e) {
throw new RuntimeException(e);
}
try (BufferedReader br = getBufferedReader(fileName)) {
//skip the first line and the columns length to get the data
//columns are identified as being splittable on the delimiter
dataRows = br.lines().skip(1 + columnNames.size()).map(s -> s.split("\t"))
.collect(toList());
} catch (Exception e) {
throw new RuntimeException(e);
}
String columns = columnNames.stream().collect(joining(",","(",")"));
List<String> dataRow = dataRows.stream()
.map(arr -> Arrays.stream(arr).map(x -> "'" + x + "'").collect(joining(",", "(", ")")))
.map(row -> String.format("INSERT INTO %s %s VALUES %s;", tableName, columns, row))
.collect(toList());
dataRow.forEach(l -> System.out.println(l));
});
}
private static BufferedReader getBufferedReader(String fileName) {
return new BufferedReader(new InputStreamReader(StatementGeneratorMain.class.getClassLoader().getResourceAsStream(
fileName)));
}
}
This piece of code does the job for me, but I don't really like it because I read the same file thrice (once for table name, again to deduce the columns, again to get the rows). I also don't think that it is proper functional style.
What I am looking for is a more elegant way to do this kind of multiline/multirecord parsing using the streams API.
What I thought about:
- Using
Files.lines()
instead ofBufferedReader
. - Using
partitioningBy()
instead offilter().collect(toList())
when parsing the columns, then true values goes into the column list and false values goes into the dataRows list.
I'm not too particular about stuff like numeric column and null values at this point.