1 /*** 2 * Copyright (C) 2008 rweber <quietgenie@users.sourceforge.net> 3 * 4 * This file is part of CsvObjectMapper. 5 * 6 * CsvObjectMapper is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU Lesser General Public License as published by 8 * the Free Software Foundation, either version 3 of the License, or 9 * (at your option) any later version. 10 * 11 * CsvObjectMapper is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU Lesser General Public License for more details. 15 * 16 * You should have received a copy of the GNU Lesser General Public License 17 * along with CsvObjectMapper. If not, see <http://www.gnu.org/licenses/>. 18 */ 19 20 /*** 21 * 22 */ 23 package com.projectnine.csvmapper; 24 25 import java.io.FileWriter; 26 import java.io.IOException; 27 import java.io.Writer; 28 import java.util.Collections; 29 import java.util.Comparator; 30 import java.util.Iterator; 31 import java.util.List; 32 import java.util.Vector; 33 34 import net.sf.csv4j.CSVWriter; 35 36 import org.apache.commons.jexl.Expression; 37 import org.apache.commons.jexl.ExpressionFactory; 38 import org.apache.commons.jexl.JexlContext; 39 import org.apache.commons.jexl.JexlHelper; 40 import org.apache.commons.logging.Log; 41 import org.apache.commons.logging.LogFactory; 42 43 /*** 44 * @author robweber 45 * 46 */ 47 public class ObjectToCsvMapper { 48 private static final Log log = LogFactory.getLog(ObjectToCsvMapper.class); 49 50 private String mappingDefinition; 51 private Writer output; 52 private CSVWriter csvWriter; 53 54 public ObjectToCsvMapper(String mappingDefinition, String outputFilename) { 55 this.mappingDefinition = mappingDefinition; 56 try { 57 this.output = new FileWriter(outputFilename); 58 csvWriter = new CSVWriter(output); 59 } catch (IOException e) { 60 log.error("Unable to open the specified file, " + outputFilename 61 + ", for writing.", e); 62 throw new RuntimeException(e); 63 } 64 } 65 66 public void writeObjectsToCsv(List<? extends Object> list) throws Exception { 67 writeObjectsToCsv(list, false); 68 } 69 70 public void writeObjectsToCsv(List<? extends Object> list, 71 boolean writeHeader) throws Exception { 72 if (writeHeader) { 73 List<String> csvHeader = createHeader(mappingDefinition); 74 if (csvHeader != null) { 75 csvWriter.writeLine(csvHeader); 76 } 77 } 78 79 for (int i = 0; i < list.size(); i++) { 80 if (list.get(i) != null) { 81 List<String> stringList = convertObjectToCsv(list.get(i), 82 mappingDefinition); 83 csvWriter.writeLine(stringList); 84 } 85 } 86 87 try { 88 output.flush(); 89 output.close(); 90 } catch (Exception e) { 91 log.warn("Unable to close the CSV output."); 92 } 93 } 94 95 public static List<String> createHeader(String mappingDefinition) { 96 List<String> csvHeader = new Vector<String>(); 97 CsvMappingDefinition csvMappingDefinition = CsvToObjectMapper.csvMappingDefinitions 98 .get(mappingDefinition); 99 100 for (int i = 0; i < csvMappingDefinition.getFieldMappings().size(); i++) { 101 String fieldHeader = csvMappingDefinition.getFieldMappings().get(i) 102 .getCsvFieldHeader(); 103 if (fieldHeader == null) { 104 fieldHeader = ""; 105 } 106 107 csvHeader.add(fieldHeader); 108 } 109 110 return csvHeader; 111 } 112 113 @SuppressWarnings("unchecked") 114 public static List<String> convertObjectToCsv(Object object, 115 String mappingDefinition) throws Exception { 116 log.debug("Converting object of class, " + object.getClass().getName() 117 + ", using mapping definition, " + mappingDefinition); 118 CsvMappingDefinition csvMappingDefinition = CsvToObjectMapper.csvMappingDefinitions 119 .get(mappingDefinition); 120 121 if (csvMappingDefinition == null) { 122 throw new Exception("The mapping definition specified, " 123 + mappingDefinition + ", is not valid."); 124 } 125 126 List<CsvFieldMapping> fieldMappingList = getFieldMappingListForMappingDefinition(mappingDefinition); 127 log.debug("Retrieved " + fieldMappingList.size() + " " 128 + (fieldMappingList.size() != 1 ? "fields" : "field") + "."); 129 List<String> csvFieldList = new Vector<String>(); 130 Collections.sort(fieldMappingList, new Comparator<CsvFieldMapping>() { 131 132 public int compare(CsvFieldMapping o1, CsvFieldMapping o2) { 133 if (o1.getColumnIndex() < o2.getColumnIndex()) { 134 return -1; 135 } 136 137 if (o1.getColumnIndex() == o2.getColumnIndex()) { 138 return 0; 139 } 140 141 return 1; 142 } 143 144 }); 145 146 if (log.isDebugEnabled()) { 147 for (int i = 0; i < fieldMappingList.size(); i++) { 148 log.debug("FieldMapping " 149 + fieldMappingList.get(i).getColumnIndex()); 150 } 151 } 152 153 int lastCsvIndex; 154 int currentFieldMappingIndex = 0; 155 if (csvMappingDefinition.getExpectedNumberOfFields() > 0) { 156 log 157 .debug("Getting expected number of fields from explicit definition."); 158 lastCsvIndex = csvMappingDefinition.getExpectedNumberOfFields(); 159 } else { 160 log.debug("Getting expected number of fields by inferrence."); 161 lastCsvIndex = fieldMappingList.get(fieldMappingList.size() - 1) 162 .getColumnIndex() + 1; 163 } 164 165 log.debug("Expect the CSV to have " + lastCsvIndex + " " 166 + (lastCsvIndex != 1 ? "fields" : "field") + "."); 167 168 JexlContext jexlContext = JexlHelper.createContext(); 169 jexlContext.getVars().put(csvMappingDefinition.getBeanVariableName(), 170 object); 171 if (csvMappingDefinition.getExtendedContext() != null) { 172 Iterator<String> it = csvMappingDefinition.getExtendedContext() 173 .keySet().iterator(); 174 while (it.hasNext()) { 175 String variableName = it.next(); 176 String valueExpression = csvMappingDefinition 177 .getExtendedContext().get(variableName); 178 Expression preProcessing = ExpressionFactory 179 .createExpression(valueExpression); 180 preProcessing.addPreResolver(new PoundDefineJexlResolver()); 181 jexlContext.getVars().put(variableName, 182 preProcessing.evaluate(jexlContext)); 183 } 184 } 185 186 for (int i = 0; i < lastCsvIndex; i++) { 187 if (fieldMappingList.get(currentFieldMappingIndex).getColumnIndex() != i) { 188 csvFieldList.add(""); 189 } else { 190 String expressionString = fieldMappingList.get( 191 currentFieldMappingIndex).getObjectToCsvExpression(); 192 Expression expression = ExpressionFactory 193 .createExpression(expressionString); 194 csvFieldList.add(fieldMappingList.get(currentFieldMappingIndex) 195 .getCsvFieldValueFromObject( 196 expression.evaluate(jexlContext))); 197 currentFieldMappingIndex++; 198 199 if (currentFieldMappingIndex < fieldMappingList.size()) { 200 while (fieldMappingList.get(currentFieldMappingIndex) 201 .getColumnIndex() == i) { 202 currentFieldMappingIndex++; 203 } 204 } 205 } 206 } 207 208 return csvFieldList; 209 } 210 211 private static List<CsvFieldMapping> getFieldMappingListForMappingDefinition( 212 String mappingDefinition) { 213 CsvMappingDefinition csvMappingDefinition = CsvToObjectMapper.csvMappingDefinitions 214 .get(mappingDefinition); 215 List<CsvFieldMapping> rawList = csvMappingDefinition.getFieldMappings(); 216 log.debug("The raw list of field mappings has " + rawList.size() + " " 217 + (rawList.size() != 1 ? "elements" : "element") + "."); 218 List<CsvFieldMapping> finishedList = new Vector<CsvFieldMapping>(); 219 220 for (int i = 0; i < rawList.size(); i++) { 221 if (rawList.get(i).getBeanName() == null) { 222 finishedList.add(rawList.get(i)); 223 } else { 224 log.debug("Complex bean definition. Getting fields for " 225 + rawList.get(i).getBeanName()); 226 List<CsvFieldMapping> deeperRawList = getFieldMappingListForMappingDefinition( 227 rawList.get(i).getBeanName()); 228 log.debug("The next bean, " + rawList.get(i).getBeanName() 229 + ", gave us " + deeperRawList.size() 230 + " more field mappings."); 231 for (int j = 0; j < deeperRawList.size(); j++) { 232 CsvFieldMapping temp = (CsvFieldMapping) deeperRawList.get( 233 j).clone(); 234 String newExpression = new StringBuffer(temp 235 .getObjectToCsvExpression()).toString(); 236 temp.setObjectToCsvExpression(newExpression.replace( 237 csvMappingDefinition.getBeanVariableName(), rawList 238 .get(i).getObjectToCsvExpression())); 239 finishedList.add(temp); 240 } 241 } 242 } 243 244 return finishedList; 245 } 246 }