/**
* Copyright (C) 2008 rweber <quietgenie@users.sourceforge.net>
*
* This file is part of CsvObjectMapper.
*
* CsvObjectMapper is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* CsvObjectMapper is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with CsvObjectMapper. If not, see <http://www.gnu.org/licenses/>.
*/
/**
*
*/
package com.projectnine.csvmapper;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import net.sf.csv4j.CSVWriter;
import org.apache.commons.jexl.Expression;
import org.apache.commons.jexl.ExpressionFactory;
import org.apache.commons.jexl.JexlContext;
import org.apache.commons.jexl.JexlHelper;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
/**
* @author robweber
*
*/
public class ObjectToCsvMapper {
private static final Log log = LogFactory.getLog(ObjectToCsvMapper.class);
private String mappingDefinition;
private Writer output;
private CSVWriter csvWriter;
public ObjectToCsvMapper(String mappingDefinition, String outputFilename) {
this.mappingDefinition = mappingDefinition;
try {
this.output = new FileWriter(outputFilename);
csvWriter = new CSVWriter(output);
} catch (IOException e) {
log.error("Unable to open the specified file, " + outputFilename
+ ", for writing.", e);
throw new RuntimeException(e);
}
}
public void writeObjectsToCsv(List<? extends Object> list) throws Exception {
writeObjectsToCsv(list, false);
}
public void writeObjectsToCsv(List<? extends Object> list,
boolean writeHeader) throws Exception {
if (writeHeader) {
List<String> csvHeader = createHeader(mappingDefinition);
if (csvHeader != null) {
csvWriter.writeLine(csvHeader);
}
}
for (int i = 0; i < list.size(); i++) {
if (list.get(i) != null) {
List<String> stringList = convertObjectToCsv(list.get(i),
mappingDefinition);
csvWriter.writeLine(stringList);
}
}
try {
output.flush();
output.close();
} catch (Exception e) {
log.warn("Unable to close the CSV output.");
}
}
public static List<String> createHeader(String mappingDefinition) {
List<String> csvHeader = new Vector<String>();
CsvMappingDefinition csvMappingDefinition = CsvToObjectMapper.csvMappingDefinitions
.get(mappingDefinition);
for (int i = 0; i < csvMappingDefinition.getFieldMappings().size(); i++) {
String fieldHeader = csvMappingDefinition.getFieldMappings().get(i)
.getCsvFieldHeader();
if (fieldHeader == null) {
fieldHeader = "";
}
csvHeader.add(fieldHeader);
}
return csvHeader;
}
@SuppressWarnings("unchecked")
public static List<String> convertObjectToCsv(Object object,
String mappingDefinition) throws Exception {
log.debug("Converting object of class, " + object.getClass().getName()
+ ", using mapping definition, " + mappingDefinition);
CsvMappingDefinition csvMappingDefinition = CsvToObjectMapper.csvMappingDefinitions
.get(mappingDefinition);
if (csvMappingDefinition == null) {
throw new Exception("The mapping definition specified, "
+ mappingDefinition + ", is not valid.");
}
List<CsvFieldMapping> fieldMappingList = getFieldMappingListForMappingDefinition(mappingDefinition);
log.debug("Retrieved " + fieldMappingList.size() + " "
+ (fieldMappingList.size() != 1 ? "fields" : "field") + ".");
List<String> csvFieldList = new Vector<String>();
Collections.sort(fieldMappingList, new Comparator<CsvFieldMapping>() {
public int compare(CsvFieldMapping o1, CsvFieldMapping o2) {
if (o1.getColumnIndex() < o2.getColumnIndex()) {
return -1;
}
if (o1.getColumnIndex() == o2.getColumnIndex()) {
return 0;
}
return 1;
}
});
if (log.isDebugEnabled()) {
for (int i = 0; i < fieldMappingList.size(); i++) {
log.debug("FieldMapping "
+ fieldMappingList.get(i).getColumnIndex());
}
}
int lastCsvIndex;
int currentFieldMappingIndex = 0;
if (csvMappingDefinition.getExpectedNumberOfFields() > 0) {
log
.debug("Getting expected number of fields from explicit definition.");
lastCsvIndex = csvMappingDefinition.getExpectedNumberOfFields();
} else {
log.debug("Getting expected number of fields by inferrence.");
lastCsvIndex = fieldMappingList.get(fieldMappingList.size() - 1)
.getColumnIndex() + 1;
}
log.debug("Expect the CSV to have " + lastCsvIndex + " "
+ (lastCsvIndex != 1 ? "fields" : "field") + ".");
JexlContext jexlContext = JexlHelper.createContext();
jexlContext.getVars().put(csvMappingDefinition.getBeanVariableName(),
object);
if (csvMappingDefinition.getExtendedContext() != null) {
Iterator<String> it = csvMappingDefinition.getExtendedContext()
.keySet().iterator();
while (it.hasNext()) {
String variableName = it.next();
String valueExpression = csvMappingDefinition
.getExtendedContext().get(variableName);
Expression preProcessing = ExpressionFactory
.createExpression(valueExpression);
preProcessing.addPreResolver(new PoundDefineJexlResolver());
jexlContext.getVars().put(variableName,
preProcessing.evaluate(jexlContext));
}
}
for (int i = 0; i < lastCsvIndex; i++) {
if (fieldMappingList.get(currentFieldMappingIndex).getColumnIndex() != i) {
csvFieldList.add("");
} else {
String expressionString = fieldMappingList.get(
currentFieldMappingIndex).getObjectToCsvExpression();
Expression expression = ExpressionFactory
.createExpression(expressionString);
csvFieldList.add(fieldMappingList.get(currentFieldMappingIndex)
.getCsvFieldValueFromObject(
expression.evaluate(jexlContext)));
currentFieldMappingIndex++;
if (currentFieldMappingIndex < fieldMappingList.size()) {
while (fieldMappingList.get(currentFieldMappingIndex)
.getColumnIndex() == i) {
currentFieldMappingIndex++;
}
}
}
}
return csvFieldList;
}
private static List<CsvFieldMapping> getFieldMappingListForMappingDefinition(
String mappingDefinition) {
CsvMappingDefinition csvMappingDefinition = CsvToObjectMapper.csvMappingDefinitions
.get(mappingDefinition);
List<CsvFieldMapping> rawList = csvMappingDefinition.getFieldMappings();
log.debug("The raw list of field mappings has " + rawList.size() + " "
+ (rawList.size() != 1 ? "elements" : "element") + ".");
List<CsvFieldMapping> finishedList = new Vector<CsvFieldMapping>();
for (int i = 0; i < rawList.size(); i++) {
if (rawList.get(i).getBeanName() == null) {
finishedList.add(rawList.get(i));
} else {
log.debug("Complex bean definition. Getting fields for "
+ rawList.get(i).getBeanName());
List<CsvFieldMapping> deeperRawList = getFieldMappingListForMappingDefinition(
rawList.get(i).getBeanName());
log.debug("The next bean, " + rawList.get(i).getBeanName()
+ ", gave us " + deeperRawList.size()
+ " more field mappings.");
for (int j = 0; j < deeperRawList.size(); j++) {
CsvFieldMapping temp = (CsvFieldMapping) deeperRawList.get(
j).clone();
String newExpression = new StringBuffer(temp
.getObjectToCsvExpression()).toString();
temp.setObjectToCsvExpression(newExpression.replace(
csvMappingDefinition.getBeanVariableName(), rawList
.get(i).getObjectToCsvExpression()));
finishedList.add(temp);
}
}
}
return finishedList;
}
}