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 }