001    /*
002     * CSVFormatterFactory.java
003     *
004     * Copyright (C) 2005 Anupam Sengupta (anupamsg@users.sourceforge.net)
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.formatters;
023    
024    import net.sf.anupam.csv.exceptions.CSVOException;
025    import org.apache.commons.logging.Log;
026    import org.apache.commons.logging.LogFactory;
027    
028    import java.util.HashMap;
029    import java.util.Map;
030    
031    /**
032     * A singleton factory which creates and caches the
033     * {@link CSVFieldFormatter csv field formatters}. The factory
034     * maintains a cache of CSV formatters that are reentrant (i.e.,
035     * the formatters that do not maintain any instance specific state).
036     *
037     * @author Anupam Sengupta
038     * @version $Revision: 1.3 $
039     * @see CSVFieldFormatter
040     * @since 1.5
041     */
042    public final class CSVFormatterFactory {
043    
044        /**
045         * The CSV formatter mapping file name. This file assumed to be present in the
046         * classpath.
047         */
048        private static final String FMT_MAPPING_FILE_NAME = "net/sf/anupam/csv/formatters/csv-formatter-config.xml";
049    
050        /**
051         * The singleton instance of the factory.
052         */
053        private static CSVFormatterFactory singleton;
054    
055        /**
056         * The generic NO-OP formatter which is used when no explicit formatter is
057         * defined.
058         */
059        private static final CSVFieldFormatter DO_NOTHING_FORMATTER = new DoNothingFormatter();
060    
061        /**
062         * The logger to use.
063         */
064        private static final Log LOG = LogFactory
065                .getLog(CSVFormatterFactory.class);
066    
067        /**
068         * Mapping of the formatter name and the configuration.
069         */
070        private Map<String, FormatterConfiguration> formatterLookupMap;
071    
072        /**
073         * The cached formatters.
074         */
075        private Map<String, CSVFieldFormatter> formatterCache;
076    
077    
078        /**
079         * Constructor for CSVFormatterFactory. Private to prevent direct
080         * instantiation.
081         */
082        private CSVFormatterFactory() {
083            super();
084            formatterLookupMap = new HashMap<String, FormatterConfiguration>();
085            formatterCache = new HashMap<String, CSVFieldFormatter>();
086        }
087    
088        /**
089         * Returns the singleton instance of this factory.
090         *
091         * @return the singleton instance
092         */
093        public synchronized static CSVFormatterFactory getSingleton() {
094            if (singleton == null) {
095                singleton = new CSVFormatterFactory();
096                singleton.loadMappings();
097                LOG.info("Created the CSVFormatter Factory");
098            }
099            return singleton;
100        }
101    
102        /**
103         * Loads all mappings from the formatter configuration file.
104         */
105        private void loadMappings() {
106            final CSVFormatterConfigParser parser = CSVFormatterConfigParser.getConfigParser();
107            final FormatterConfiguration doNothingConfiguration = new FormatterConfiguration();
108            doNothingConfiguration.setFormatterName("none");
109            doNothingConfiguration.setFormatterClass("net.sf.anupam.csv.formatters.DoNothingFormatter");
110            doNothingConfiguration.setConstructionNeeded(false);
111            formatterLookupMap.put("none", doNothingConfiguration);
112            formatterLookupMap.putAll(parser.getFormatMappings(FMT_MAPPING_FILE_NAME,
113                    true));
114            createCache();
115            LOG.debug("Loaded the CSV Mapping configuration from "
116                    + FMT_MAPPING_FILE_NAME);
117        }
118    
119        /**
120         * Creates the cached formatters for the ones which do not need special
121         * construction.
122         */
123        private void createCache() {
124            for (String formatterName : formatterLookupMap.keySet()) {
125                final FormatterConfiguration currentFormatter = formatterLookupMap
126                        .get(formatterName);
127                // If the formatter does not require special construction,
128                // then create it one time and cache it.
129                if (!currentFormatter.isConstructionNeeded()) {
130    
131                    final CSVFieldFormatter formatter = createFormatterForClass(currentFormatter
132                            .getFormatterClass());
133    
134                    formatterCache.put(formatterName, formatter);
135                }
136            }
137        }
138    
139        /**
140         * Creates a formatter from the specified class.
141         *
142         * @param className the formatter class
143         * @return the created formatter
144         */
145        private CSVFieldFormatter createFormatterForClass(final String className) {
146    
147            Object formatter;
148            try {
149                formatter = Class.forName(className.trim()).newInstance();
150            } catch (final InstantiationException e) {
151                LOG.warn("Could not create formatter for class: "
152                        + className, e);
153                formatter = DO_NOTHING_FORMATTER;
154            } catch (final IllegalAccessException e) {
155                LOG.warn("Could not create formatter for class: "
156                        + className, e);
157                formatter = DO_NOTHING_FORMATTER;
158            } catch (final ClassNotFoundException e) {
159                LOG.warn("Could not create formatter for class: "
160                        + className, e);
161                formatter = DO_NOTHING_FORMATTER;
162            }
163            return (CSVFieldFormatter) formatter;
164    
165        }
166    
167        /**
168         * Creates a new instance of the specified formatter. The cache is used
169         * whenever possible.
170         *
171         * @param formatterName the formatter to return
172         * @return the requested formatter
173         * @throws CSVOException thrown if the formatter cannot be created
174         */
175        public CSVFieldFormatter createFormatterFor(final String formatterName)
176                throws CSVOException {
177    
178            // If a cache hit, then return the cached formatter
179            if (formatterCache.containsKey(formatterName)) {
180                return formatterCache.get(formatterName);
181            } else {
182                LOG.warn("Formatter: " + formatterName + " not found");
183                throw new CSVOException("Formatter: " + formatterName + " not found");
184            }
185    
186        }
187    }