001 /* 002 * CSVParser.java 003 * 004 * Copyright (C) 2005 Anupam Sengupta ([email protected]) 005 * 006 * This program is free software; you can redistribute it and/or 007 * modify it under the terms of the GNU General Public License 008 * as published by the Free Software Foundation; either version 2 009 * of the License, or (at your option) any later version. 010 * 011 * This program is distributed in the hope that it will be useful, 012 * but WITHOUT ANY WARRANTY; without even the implied warranty of 013 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 014 * GNU General Public License for more details. 015 * 016 * You should have received a copy of the GNU General Public License 017 * along with this program; if not, write to the Free Software 018 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 019 * 020 * Version: $Revision: 1.3 $ 021 */ 022 package net.sf.anupam.csv; 023 024 import net.sf.anupam.csv.formatters.CSVFieldFormatter; 025 import net.sf.anupam.csv.mapping.CSVBeanMapping; 026 import net.sf.anupam.csv.mapping.CSVFieldMapping; 027 import org.apache.commons.beanutils.BeanUtils; 028 import org.apache.commons.lang.builder.ToStringBuilder; 029 import org.apache.commons.logging.Log; 030 import org.apache.commons.logging.LogFactory; 031 032 import java.lang.reflect.InvocationTargetException; 033 import java.util.Iterator; 034 import java.util.List; 035 036 /** 037 * Parses CSV files and creates the mapped POJO objects. This is the primary 038 * interface into the CSV parsing framework. 039 * <p/> 040 * The class implements {@link Iterable Iterable} interface and can be 041 * used in the new <em>Tiger</em> for loops to iterate over all the CSV 042 * records in the file. 043 * </p> 044 * <p/> 045 * Configuration of the parser is performed via the <code>csv-mapping.xml</code> 046 * file. See the package description for more details. 047 * </p> 048 * <p/> 049 * Note that the class is not meant to be instantiated directly. Instead, the 050 * {@link CSVParserFactory CSVParserFactory} factory should be 051 * used for creation of instances. 052 * </p> 053 * 054 * @author Anupam Sengupta 055 * @version $Revision: 1.3 $ 056 * @see CSVParserFactory 057 * @since 1.5 058 */ 059 public class CSVParser implements Iterable<Object> { 060 061 /** 062 * The logger to use. 063 */ 064 private static final Log LOG = LogFactory.getLog(CSVParser.class); 065 066 /** 067 * The CSV Reader to use for this parser. 068 */ 069 private CSVReader reader; 070 071 /** 072 * The root bean mapping configuration for this parser. 073 */ 074 private CSVBeanMapping rootBeanMapping; 075 076 /** 077 * Constructor for CSVParser. The constructor accepts the bean mapping to 078 * use as the starting CSV mapping configuration 079 * <em>(a.k.a the root bean mapping)</em> and the CSV reader/parser engine 080 * to use for actual parsing. 081 * 082 * @param rootBeanMapping the bean mapping to use as the starting configuration 083 * @param reader the CSV Reader object which will actually parse the CSV file 084 */ 085 public CSVParser(final CSVBeanMapping rootBeanMapping, 086 final CSVReader reader) { 087 super(); 088 this.rootBeanMapping = rootBeanMapping; 089 this.reader = reader; 090 } 091 092 /** 093 * Dumps the root bean mapping configuration for this parser. This is meant 094 * for <strong>debugging</strong> only. 095 * 096 * @return the string representation of this parser 097 * @see Object#toString() 098 */ 099 @Override 100 public String toString() { 101 return new ToStringBuilder(this).append("beanMapping", rootBeanMapping) 102 .toString(); 103 } 104 105 /** 106 * Finalizes this parser and closes the reader. 107 * 108 * @throws Throwable thrown if the finalization fails 109 * @see Object#finalize() 110 */ 111 @Override 112 protected void finalize() throws Throwable { 113 super.finalize(); 114 if (reader != null) { 115 reader.close(); 116 reader = null; 117 } 118 rootBeanMapping = null; 119 } 120 121 /** 122 * The iterator to provide the Iterable interface to the parser. 123 */ 124 private final class MappedObjectIterator implements Iterator<Object> { 125 126 /** 127 * The actual line iterator to use. 128 */ 129 private Iterator<List<String>> csvLineIter; 130 131 /** 132 * The iterator constructor. 133 * 134 * @param csvLineIter The actual line iterator to use 135 */ 136 MappedObjectIterator(final Iterator<List<String>> csvLineIter) { 137 super(); 138 this.csvLineIter = csvLineIter; 139 } 140 141 /** 142 * Finalizes this iterator and nullifies all instance variables. 143 * 144 * @throws Throwable if the finalization fails 145 * @see Object#finalize() 146 */ 147 @Override 148 protected final void finalize() throws Throwable { 149 super.finalize(); 150 csvLineIter = null; 151 } 152 153 /** 154 * Indicates whether more parsed POJO beans exist. 155 * 156 * @return indicates whether there are any more parsed beans 157 * @see java.util.Iterator#hasNext() 158 */ 159 public final boolean hasNext() { 160 return csvLineIter.hasNext(); 161 } 162 163 /** 164 * Returns the parsed and mapped POJO bean corresponding to the current 165 * CSV line. Each subsequent invocation will parse and return the next 166 * parsed POJO, until end of the CSV stream is reached. 167 * 168 * @return the parsed bean 169 * @see java.util.Iterator#next() 170 */ 171 public Object next() { 172 final List<String> csvLine = csvLineIter.next(); 173 return getMappedBean(csvLine, getRootBeanMapping()); 174 } 175 176 /** 177 * This operation is not supported. 178 * 179 * @see java.util.Iterator#remove() 180 */ 181 public final void remove() { 182 csvLineIter.remove(); 183 } 184 185 /** 186 * Applies the field formatters if present. 187 * 188 * @param csvFieldValue the field to format 189 * @param fieldMapping the field mapping from which the formatter should be used 190 * @return the formatted value 191 */ 192 private String formatValue(final String csvFieldValue, 193 final CSVFieldMapping fieldMapping) { 194 final CSVFieldFormatter formatter = fieldMapping.getFormatter(); 195 if (formatter == null) { 196 return csvFieldValue; 197 } 198 199 return formatter.format(csvFieldValue); 200 } 201 202 /** 203 * Returns the mapped bean from the specified list of CSV values. 204 * 205 * @param csvLine the CSV line to parse 206 * @param beanMap the bean mapping to use 207 * @return the mapped bean 208 */ 209 private Object getMappedBean(final List<String> csvLine, 210 final CSVBeanMapping beanMap) { 211 212 try { 213 final Object bean = Class.forName(beanMap.getBeanClass()) 214 .newInstance(); 215 216 for (CSVFieldMapping fieldMapping : beanMap) { 217 final Object formattedFieldValue; 218 219 if (fieldMapping.getBeanReferenceName().equals("none")) { 220 formattedFieldValue = getMappedField(csvLine, 221 fieldMapping); 222 223 } else { 224 // Recurse and get the value. 225 formattedFieldValue = getMappedBean(csvLine, 226 fieldMapping.getBeanReference()); 227 } 228 229 try { 230 BeanUtils.setProperty(bean, fieldMapping 231 .getAttributeName(), formattedFieldValue); 232 } catch (final IllegalAccessException e) { 233 LOG.warn(e); 234 } catch (final InvocationTargetException e) { 235 LOG.warn(e); 236 } 237 } 238 return bean; 239 240 } catch (final ClassNotFoundException e) { 241 LOG.warn("The Bean for class: " + beanMap.getClass() 242 + " could not be instantiated", e); 243 return null; 244 245 } catch (final IllegalAccessException e) { 246 LOG.warn("The Bean for class: " + beanMap.getClass() 247 + " could not be instantiated", e); 248 return null; 249 } catch (final InstantiationException e) { 250 LOG.warn("The Bean for class: " + beanMap.getClass() 251 + " could not be instantiated", e); 252 return null; 253 } 254 } 255 256 /** 257 * Returns the parsed field value. 258 * 259 * @param csvLine the CSV line to parse 260 * @param fieldMapping the field mapping to use 261 * @return the mapped field value 262 */ 263 private Object getMappedField(final List<String> csvLine, 264 final CSVFieldMapping fieldMapping) { 265 266 final String csvFieldValue = csvLine.get(fieldMapping 267 .getFieldPosition()); 268 return formatValue(csvFieldValue, fieldMapping); 269 270 } 271 272 } 273 274 /** 275 * Returns the iterator for retrieving the parsed POJO beans. 276 * 277 * @return the iterator over the parsed beans 278 * @see Iterable#iterator() 279 */ 280 public Iterator<Object> iterator() { 281 282 return new MappedObjectIterator(reader.iterator()); 283 } 284 285 /** 286 * Returns the root bean mapping. The root bean mapping is the bean mapping 287 * with which the Parser is configured. "Child" bean mappings (which are not 288 * directly accessible) are the bean mapping configurations which may be 289 * present as references from the root mapping. 290 * 291 * @return Returns the root bean mapping. 292 */ 293 private CSVBeanMapping getRootBeanMapping() { 294 return this.rootBeanMapping; 295 } 296 297 }