I wanted to review my design strategy for CSV parser.
I have 4 CSV files, which have different layouts as shown below.
Each row of a CSV file will be mapped to a class. For example, if CSV_FILE_A
has 10 rows, a list contains 10 objects of ObjA will be returned after calling parser function. For CSV_FILE_B of 5 entries, a list of 5 ObjB will be returned.
CSV_FILE_A: LONG, STR, STR
CSV_FILE_B: LONG, LONG
CSV_FILE_C: LONG, LONG, STR, LONG
CSV_FILE_D: LONG, LONG, LONG
I designed an interface like this.
public interface CSVParser<T> {
public List<T> parseCSVFile(String fileName);
}
And I have created 4 different parsers, which implement the interface and have a different logic of parsing the CSV file. The code below is a complete implementation for CSV_FILE_B. The other three parsers will have the same structures but different if-else
statements for setting object fields and different object types in the type of List.
public class csvBParser implements CSVParser {
@Override
public List<ObjectB> parseCSVFile(String fileName) {
List<ObjectB> list = new ArrayList<ObjectB>();
try {
ClassLoader classLoader = Thread.currentThread()
.getContextClassLoader();
InputStream is = classLoader.getResourceAsStream(fileName);
BufferedReader br = new BufferedReader(new InputStreamReader(is));
String line = "";
StringTokenizer token = null;
int lineNum = 0, tokenNum = 0;
while ((line = br.readLine()) != null) {
if (lineNum == 0){
lineNum++;
continue;
}
token = new StringTokenizer(line, ",");
ObjectB objB = new ObjectB();
while (token.hasMoreTokens()) {
String nextToken = token.nextToken();
boolean isTokenNull = false;
if (nextToken.equalsIgnoreCase("null"))
isTokenNull = true;
if (!isTokenNull) {
if (tokenNum == 0) {
objB.setObjBId(Long.valueOf(nextToken));
} else if (tokenNum == 1) {
objB.setObjBSndId(Long.valueOf(nextToken));
} else {
System.err.println("Invalid Token Number "
+ tokenNum);
}
}
tokenNum++;
}
list.add(objB);
tokenNum = 0;
lineNum++;
}
} catch (Exception e) {
System.err.println("Parse Error " + e.getMessage());
}
return list;
}
}
And, I finally uses a factory to create an instance of a parser.
public class CSVParserFactory {
public static CSVParser getParser(TableType type) {
CSVParser parser = null;
if(type == A){
parser = new csvAParser();
}
else if(type == B){
parser = new csvBParser();
}
else if(type == C){
parser = new csvCParser();
}
else if(type == D){
parser = new csvDParser();
}
/*
* more parsers will be added later
*/
else{
throw new IllegalArgumentException ("No such table type");
}
return parser;
}
}
The parser is being called like this. Each element in the list is corresponding to a row in the CSV file
CSVParser parser = CSVParserFactory.getParser(TableType.A);
List list = parser.parseCSVFile(CSV_FILE_A);
Do you guys have any suggestions of this design pattern? If I consider a code factorization, what do I need to do?
Thanks in advance.