Salut,
J'ai continué les TU sur nuiton-csv, et notamment tester le ModelBuilder.
Mais j'ai un doute de son utilité.
En gardant l'idée de l'api, j'ai fais une implem un peu plus pousser avec
une methode buildImportModel qui te renvoie directement
un ImportModel avec les différentes colonnes construites.
Finalement il serait plus simple que le ModelBuilder implémente les deux
interfaces ImportModel et ExportModel, on pourrait le renommer en CsvModel.
Pour les besoins spécifiques rien n'empêchera l'héritage. Ainsi
pushCsvHeaderNames sera vide par défaut et étendu au besoin (d'ailleurs je
la renommerais bien en onReadHeaders pour montrer son côté callback).
Même chose pour le newInstance, en passant la classe au constructeur du
modèle, on peut proposer une implem par défaut avec un myClass.newInstance.
Libre à l'utilisateur de surcharger s'il veut un comportement plus pousser
(utiliser sa propre factory). Une autre idée serait de proposer des
contrats pour avoir un callback sur les headers et une factory pour
l'instanciation.
Je reviens aussi sur les interfaces ImportableColumn et ExportableColumn
que je trouve inutiles. Il faut juste permettre le !mandatory & !ignored
dans la classe Column.
Pour l'Import je rajouterais bien aussi des méthodes qui l’exécute
directement sans avoir besoin d'instance d'Import. Par exemple :
public static <E> List<E> getResultAsList(ImportModel<E> model,
Reader reader) {
Import<E> instance = Import.newImport(model, reader);
try {
List<E> result = new ArrayList<E>();
for (E row : instance) {
result.add(row);
}
return result;
} finally {
instance.close();
}
}
En pratique on pourrait avoir :
CsvModel<Row> model = CsvModel.newModel(';', Row.class);
model.addMandatoryColumn("TITLE", "title");
model.addMandatoryColumn("DATE", "date", new
Common.DateValue("yyyy-MM-dd"));
model.addMandatoryColumn("NUMBER", "number", Common.INTEGER);
List<Row> results = Import.getResultAsList(model, reader);
Qu'en pensez-vous ?
je vais maintenant regarder l'Export voir si ça se goupille bien. Je pense
qu'on peut avoir une api bien sympa en tout cas, mais ya encore pas mal de
chose à voir pour simplifier/fignoler.
--------------8<----------------8<--------------8<-----------------
Exemple plus compliqué comme dans WAO :
CSV:
---
TITLE;01/2010;02/2010;03/2010
"Row1";1;2;3
"Row2";5;0;1
"Row3";4;1;0
Import:
---
CsvModel<Row> builder = CsvModel.newModel(';', Row.class);
builder.addMandatoryColumn("TITLE", "title");
builder.setHeadersCallback(new CsvModel.HeadersCallback() {
@Override
public void onReadHeaders(List<String> headerNames) {
List<String> dynamicHeaders = headerNames.subList(1,
headerNames.size());
for (String header : dynamicHeaders) {
// Format header to have Date value
Date month;
try {
month = DateUtils.parseDate(header, "MM/yyyy");
} catch (ParseException e) {
throw new ImportRuntimeException("Parsing error of
dynamic header " + header, e);
}
// Add the dynamic column using Row specified setter
builder.addMandatoryColumn(
header,
Common.INTEGER,
Row.getMonthValueSetter(month)
);
}
}
});
List<Row> results = Import.getResultAsList(model, reader);
Row :
---
public class Row {
private String title;
private SortedMap<Date, Integer> monthValues;
public Row() {
this.monthValues = new TreeMap<Date, Integer>();
}
public String getTitle() {
return title;
}
public void setTitle(String title) {
this.title = title;
}
public void addMonth(Date month, Integer value) {
monthValues.put(month, value);
}
public Map<Date, Integer> getMonthValues() {
return monthValues;
}
public static ValueSetter<Row, Integer> getMonthValueSetter(final
Date month) {
return new ValueSetter<Row, Integer>() {
@Override
public void set(Row object, Integer value) throws
Exception {
object.addMonth(month, value);
}
};
}
}