001 /* 002 * CSVParserFactory.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.formatters.CSVFormatterFactory; 026 import net.sf.anupam.csv.mapping.CSVBeanMapping; 027 import net.sf.anupam.csv.mapping.CSVFieldMapping; 028 import net.sf.anupam.csv.mapping.CSVMappingParser; 029 import net.sf.anupam.csv.exceptions.CSVOException; 030 import org.apache.commons.lang.StringUtils; 031 import org.apache.commons.logging.Log; 032 import org.apache.commons.logging.LogFactory; 033 034 import java.io.FileNotFoundException; 035 import java.io.FileReader; 036 import java.io.InputStream; 037 import java.io.InputStreamReader; 038 import java.io.Reader; 039 import java.util.HashMap; 040 import java.util.Map; 041 042 /** 043 * Singleton factory for creating the {@link CSVParser CSVParser} parser objects 044 * for clients' of the framework. This factory uses the 045 * <code>csv-mapping.xml</code> mapping configuration to create CSV parsers 046 * customized for the POJO bean to parse. This is the first interface for 047 * clients of the framework. 048 * 049 * @author Anupam Sengupta 050 * @version $Revision: 1.3 $ 051 * @see CSVParser 052 * @since 1.5 053 */ 054 public final class CSVParserFactory { 055 056 /** 057 * The Mapping file name. 058 */ 059 private static final String MAPPING_FILE_NAME = "csv-mapping.xml"; 060 061 /** 062 * The logger to use. 063 */ 064 private static final Log LOG = LogFactory.getLog(CSVParserFactory.class); 065 066 /** 067 * The singleton factory instance. 068 */ 069 private static CSVParserFactory singleton; 070 071 private static final CSVFormatterFactory FORMATTER_FACTORY = CSVFormatterFactory 072 .getSingleton(); 073 074 /** 075 * The CSV to POJO mapping repository. 076 */ 077 private Map<String, CSVBeanMapping> beanMappings; 078 079 static { 080 081 } 082 083 /** 084 * Constructor for CSVParserFactory. Private as this is a singleton. 085 */ 086 private CSVParserFactory() { 087 super(); 088 beanMappings = new HashMap<String, CSVBeanMapping>(); 089 } 090 091 /** 092 * Returns the singleton instance of this factory. 093 * 094 * @return the singleton parser factory 095 * @throws CSVOException thrown if the singleton cannot be created 096 */ 097 public synchronized static CSVParserFactory getSingleton() 098 throws CSVOException { 099 if (singleton == null) { 100 // Create the singleton at startup. 101 singleton = new CSVParserFactory(); 102 singleton.loadMappings(); 103 LOG.info("Created the Singleton for: " + CSVParserFactory.class); 104 } 105 return singleton; 106 } 107 108 /** 109 * Loads the bean mapping configuration from the XML mapping file. 110 * 111 * @throws CSVOException thrown if the mapping cannot be loaded 112 */ 113 private void loadMappings() 114 throws CSVOException { 115 final CSVMappingParser parser = new CSVMappingParser(); 116 beanMappings.putAll(parser.getMappings(MAPPING_FILE_NAME, true)); 117 118 for (String beanNames : beanMappings.keySet()) { 119 final CSVBeanMapping currentBeanMapping = beanMappings 120 .get(beanNames); 121 for (CSVFieldMapping currentFieldMapping : currentBeanMapping) { 122 createFormattersFor(currentFieldMapping); 123 resolveBeanReferencesFor(currentFieldMapping); 124 } 125 } 126 LOG.debug("Loaded the CSV Mapping configuration from " 127 + MAPPING_FILE_NAME); 128 } 129 130 /** 131 * Creates any necessary field formatters for the specified field mapping. 132 * 133 * @param fieldMapping the field for which formatters should be created 134 * @throws net.sf.anupam.csv.exceptions.CSVOException 135 * thrown if the specified formatters cannot be created 136 */ 137 private void createFormattersFor(final CSVFieldMapping fieldMapping) 138 throws CSVOException { 139 140 final CSVFieldFormatter formatter = FORMATTER_FACTORY 141 .createFormatterFor(fieldMapping.getReformatterName()); 142 fieldMapping.setFormatter(formatter); 143 144 } 145 146 /** 147 * Resolves bean references for the specified field, and sets the bean 148 * mapping hierarchy accordingly. 149 * 150 * @param fieldMapping the field for which references need to be resolved 151 */ 152 private void resolveBeanReferencesFor(final CSVFieldMapping fieldMapping) { 153 154 final String beanRefName = fieldMapping.getBeanReferenceName(); 155 if (!beanRefName.equalsIgnoreCase("none")) { 156 final CSVBeanMapping referencedBean = getBeanMapping(beanRefName); 157 158 if (referencedBean != null) { 159 fieldMapping.setBeanReference(referencedBean); 160 } else { 161 LOG.warn("For field " + fieldMapping 162 + " the referenced bean does not exist"); 163 fieldMapping.setBeanReferenceName("none"); 164 } 165 } 166 167 } 168 169 /** 170 * Returns the requested bean mapping configuration. 171 * 172 * @param beanName the POJO bean for which the mapping is to be returned 173 * @return the CSV bean mapping, or <code>null</code> if not found 174 */ 175 public CSVBeanMapping getBeanMapping(final String beanName) { 176 return beanMappings.get(beanName); 177 } 178 179 /** 180 * Returns a new CSV file parser for the specified mapping, and the 181 * specified CSV file. 182 * 183 * @param mappingName the CSV mapping to for which the parser should be created 184 * @param csvFileName the CSV file to be parsed 185 * @param inClassPath indicates whether the CSV file is in the classpath 186 * @return the CSV Parser, or <code>null</code> if not found 187 * @throws FileNotFoundException thrown if the specified CSV file cannot be found 188 * @see #getCSVParser(String,java.io.Reader) 189 */ 190 public CSVParser getCSVParser(final String mappingName, 191 final String csvFileName, final boolean inClassPath 192 ) throws FileNotFoundException { 193 194 if (StringUtils.isEmpty(csvFileName)) { 195 LOG.warn("The specified CSV Filename is empty"); 196 throw new IllegalArgumentException("File Name is empty"); 197 } 198 199 final Reader reader; 200 201 try { 202 if (inClassPath) { 203 final InputStream is = ClassLoader 204 .getSystemResourceAsStream(csvFileName); 205 if (is == null) { 206 throw new FileNotFoundException("The CSV File: " 207 + csvFileName + " was not found in the classpath"); 208 } 209 reader = new InputStreamReader(is); 210 } else { 211 reader = new FileReader(csvFileName); 212 213 } 214 LOG.debug("Successfully read the CSV file"); 215 } catch (final FileNotFoundException e) { 216 LOG.warn("The specified CSV File: " + csvFileName 217 + " was not found", e); 218 throw e; 219 } 220 221 return getCSVParser(mappingName, reader); 222 } 223 224 /** 225 * Returns a new CSV file parser for the specified mapping and the specified 226 * CSV reader stream. 227 * 228 * @param mappingName the CSV mapping for which the parser should be returned 229 * @param csvReader the CSV stream to parse 230 * @return the CSV Parser, or <code>null</code> if not found 231 * @see #getCSVParser(String,String,boolean) 232 */ 233 public CSVParser getCSVParser(final String mappingName, 234 final Reader csvReader) { 235 236 final CSVBeanMapping beanMapping = getBeanMapping(mappingName); 237 238 if (beanMapping == null) { 239 LOG.warn("Specified bean mapping was not found"); 240 throw new IllegalArgumentException( 241 "Specified bean mapping was not found"); 242 } 243 244 if (csvReader == null) { 245 LOG.warn("Specified CSV IO Reader was null"); 246 throw new IllegalArgumentException( 247 "Specified CSV IO Reader was null"); 248 } 249 250 final CSVReader reader = new CSVReader(csvReader, beanMapping 251 .isCsvHeaderPresent()); 252 253 return new CSVParser(beanMapping, reader); 254 } 255 }