View Javadoc

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 }