Итак, приступим к написанию reader-а:
/**
* @author vie
*
* TSVReader.java
* Class for read tab separated values files
*/
public class TSVReader<T> {
private final String filePath;
public TSVReader(String filePath) {
this.filePath = filePath;
}
/**
* This method reads tab separated values file
* into object of the arbitrary class
*
* @param clazz type of transmitted class
* @return List
* @throws com.site.tsv.exceptions.TSVException
*/
public List<T> read(Class<T> clazz) throws TSVException {
List<T> result = new LinkedList<T>();
try {
BufferedReader bReader = new BufferedReader(new FileReader(filePath));
String line;
//recieving all lines
int i = 1;
while ((line = bReader.readLine()) != null) {
//recieving all line members
String datavalue[] = line.split("\t");
//create instance of line entity
Constructor constructor = clazz.getConstructor(null);
Object somethingInstance = constructor.newInstance(null);
//parsing line entity members
Field[] fields = clazz.getDeclaredFields();
//recieve map of line entity member types
Map<Integer, Class<?>> mapFields = defineFields(clazz);
//for each line entity field
if (fields != null && datavalue != null && fields.length == datavalue.length) {
for (int j = 0; j < fields.length; j++) {
//for each line entity field
String s = fields[j].getName();
s = s.substring(0, 1).toUpperCase() + s.substring(1, s.length());
//exec line entity setter methods
Method method = clazz.getMethod("set" + s, mapFields.get(j));
method.invoke(somethingInstance, getDeterminatedValue(datavalue[j], fields[j].getType()));
}
} else if (fields != null && datavalue != null && fields.length != datavalue.length) {
throw new TSVInvalidFileException("Count of formatted file fields on line " + i + " not equal count of entity fields");
}
i++;
result.add((T) somethingInstance);
}
bReader.close();
} catch (FileNotFoundException e) {
throw new TSVException(e);
} catch (IOException e) {
throw new TSVException(e);
} catch (ParseException e) {
throw new TSVInvalidFileException(e);
} catch (NumberFormatException e) {
throw new TSVInvalidFileException(e);
} catch (NoSuchMethodException e) { //reflect
throw new TSVException(e);
} catch (InstantiationException e) {
throw new TSVException(e);
} catch (IllegalAccessException e) {
throw new TSVException(e);
} catch (InvocationTargetException e) {
throw new TSVException(e);
}
return result;
}
/**
* Returns field names and types
*
* @param clazz type of transmitted class
* @return Map<string,integer>.
* @throws com.site.tsv.exceptions.TSVIncorrectAnnotatedFieldsException
*/
private Map<Integer, Class<?>> defineFields(Class<?> clazz) throws TSVIncorrectAnnotatedFieldsException {
Map<Integer, Class<?>> map = new HashMap<Integer, Class<?>>();
Field[] fields = clazz.getDeclaredFields();
int i = 0;
for (Field field : fields) {
if (field.isAnnotationPresent(TSVInteger.class)) {
map.put(i++, Integer.class);
} else if (field.isAnnotationPresent(TSVDouble.class)) {
map.put(i++, Double.class);
} else if (field.isAnnotationPresent(TSVString.class)) {
map.put(i++, String.class);
} else if (field.isAnnotationPresent(TSVDate.class)) {
map.put(i++, Date.class);
}
}
if (fields.length == 0 || fields.length != map.size()) {
throw new TSVIncorrectAnnotatedFieldsException("Some field not annotated or count of fields = 0!");
}
return map;
}
/**
* Returns converted to the native type field
*
* @param original value of obtained field
* @param needType type of transmitted class
* @return Object
* @throws java.text.ParseException
*/
private Object getDeterminatedValue(String original, Class<?> needType) throws ParseException {
if (needType.getName().equals("java.lang.Integer")) {
return Integer.parseInt(original);
} else if (needType.getName().equals("java.lang.Double")) {
return Double.parseDouble(original);
} else if (needType.getName().equals("java.lang.String")) {
return original;
} else if (needType.getName().equals("java.util.Date")) {
SimpleDateFormat formatter = new SimpleDateFormat("dd.MM.yyyy");
return formatter.parse(original);
} else {
return null;
}
}
}
В классе я использовал generics, чтобы проверять заданного типы entity на этапе компиляции. В преобразовании строк с различными типами данных в entity объект мне помог reflection. Для разбора с его помощью entity, написаны приведенные ниже аннотации. Также я создал несколько исключений, чтобы в при выпадении ошибок можно было быстро понять что свалилось, будь то reflection, IO или преобразование чисел и дат.