001    package com.mockrunner.jdbc;
002    
003    import java.io.File;
004    import java.io.FileNotFoundException;
005    import java.util.HashMap;
006    import java.util.List;
007    import java.util.Map;
008    
009    import com.mockrunner.mock.jdbc.MockResultSet;
010    import com.mockrunner.util.common.FileUtil;
011    import com.mockrunner.util.common.StringUtil;
012    
013    /**
014     * Can be used to create a <code>ResultSet</code> based on
015     * a table specified in a CSV file. You can specify the delimiter
016     * of the columns (default is <code>;</code>). Furthermore you can specify if the first line
017     * contains the column names (default is <code>false</code>) and if
018     * the column entries should be trimmed (default is <code>true</code>).
019     * With {@link #setUseTemplates} you can enable template replacement in the
020     * files (default is <code>false</code>, i.e. templates are disabled).
021     * The file can be specified directly or by its name. The class
022     * tries to find the file in the absolut or relative path and
023     * (if not found) by calling <code>getResource</code>. Note that the
024     * file must exist in the local file system and cannot be loaded from
025     * inside a jar archive.
026     */
027    public class FileResultSetFactory implements ResultSetFactory
028    {
029        private File file = null;
030        private String delimiter = ";";
031        private boolean firstLineContainsColumnNames = false;
032        private boolean trim = true;
033        private boolean useTemplates = false;
034        private String templateMarker = null;
035        private Map templates = null;
036    
037        public FileResultSetFactory(String fileName)
038        {
039            this(new File(fileName));
040        }
041        
042        public FileResultSetFactory(File file)
043        {
044            this.file = file;
045            setDefaultTemplateConfiguration();
046        }
047        
048        /**
049         * Get the <code>File</code> being used to read in the 
050         * <code>ResultSet</code>. Throws a <code>RuntimeException</code>
051         * if the file does not exist.
052         * @return the file 
053         */
054        public File getFile()
055        {
056            if (file.exists() && file.isFile())
057            {
058                return file;
059            } 
060            else
061            {
062                try
063                {
064                    file = FileUtil.findFile(file.getPath());
065                    return file;
066                } 
067                catch (FileNotFoundException exc)
068                {
069                    throw new RuntimeException("Could not find: " + file.getPath());
070                }
071            }
072        }
073        
074        /**
075         * Set the delimiter. Default is <i>";"</i>.
076         * @param delimiter the delimiter
077         */
078        public void setDelimiter(String delimiter)
079        {
080            this.delimiter = delimiter;
081        }
082    
083        /**
084         * Set if the first line contains the column names.
085         * Default is <code>false</code>.
086         */
087        public void setFirstLineContainsColumnNames(boolean firstLineContainsColumnNames)
088        {
089            this.firstLineContainsColumnNames = firstLineContainsColumnNames;
090        }
091    
092        /**
093         * Set if the column entries should be trimmed.
094         * Default is <code>true</code>.
095         */
096        public void setTrim(boolean trim)
097        {
098            this.trim = trim;
099        }
100    
101        /**
102         * Set this to <code>true</code> to allow the use of templates
103         * in data files. A template is identified by a marker followed
104         * by a label. The template is replaced by a predefined string in
105         * the corresponding data file. E.g. with the default configuration,
106         * <code>$defaultString</code> is replaced by an empty string
107         * in the file.
108         * The default configuration which is automatically set uses
109         * <code>$</code> as a marker. See {@link #setDefaultTemplateConfiguration}
110         * for details. You can also set a custom template configuration using
111         * {@link #setTemplateConfiguration(String, Map)}.
112         * Default is <code>false</code>, i.e. templates are disabled.
113         * @param useTemplates set <code>true</code> to enable templates.
114         */
115        public void setUseTemplates(boolean useTemplates) 
116        {
117            this.useTemplates = useTemplates;    
118        }
119        
120        /**
121         * This method sets a custom template configuration. See 
122         * {@link #setUseTemplates} for an explanation how templates work.
123         * <code>marker + map key</code> is replaced by the corresponding <code>map
124         * value</code> in the data files.
125         * Please use {@link #setDefaultTemplateConfiguration} to set a
126         * default configuration.
127         * @param marker the custom marker replacing the default <code>$</code>
128         * @param templates the custom template map
129         */
130        public void setTemplateConfiguration(String marker, Map templates)
131        {
132            this.templates = templates;
133            this.templateMarker = marker;
134        }
135        
136        /**
137         * This method sets the default template configuration. See 
138         * {@link #setUseTemplates} for an explanation how templates work.
139         * The default marker is <code>$</code> and the default templates are:<br><br>
140         * <code>$defaultString</code> is replaced by an empty string<br>
141         * <code>$defaultDate</code> is replaced by <code>1970-01-01</code><br>
142         * <code>$defaultInteger</code> is replaced by <code>0</code><br><br>
143         * Please use {@link #setTemplateConfiguration(String, Map)} to set a
144         * custom marker and custom templates.
145         */
146        public void setDefaultTemplateConfiguration()
147        {
148            Map templates = new HashMap();
149            templates.put("defaultString", "");
150            templates.put("defaultDate", "1970-01-01");
151            templates.put("defaultInteger", "0");
152            setTemplateConfiguration("$", templates);
153        }
154    
155        public MockResultSet create(String id)
156        {
157            MockResultSet resultSet = new MockResultSet(id);
158            File fileToRead = getFile();
159            List lines = FileUtil.getLinesFromFile(fileToRead);
160    
161            int firstLineNumber = 0;
162            if(firstLineContainsColumnNames)
163            {
164                String firstLine = (String)lines.get(firstLineNumber);
165                firstLineNumber++;
166                String[] names = StringUtil.split(firstLine, delimiter, trim);
167                for(int ii = 0; ii < names.length; ii++)
168                {
169                    resultSet.addColumn(names[ii]);
170                }
171            }
172            for(int ii = firstLineNumber; ii < lines.size(); ii++)
173            {
174                String line = (String)lines.get(ii);
175                String[] values = StringUtil.split(line, delimiter, trim);
176                if(useTemplates)
177                {
178                    for(int yy = 0; yy < values.length; yy++)
179                    {
180                            if(null != values[yy])
181                            {
182                                    if(values[yy].startsWith(templateMarker) && templates.containsKey(values[yy].substring(1)))
183                                    {
184                                            values[yy] = (String)templates.get(values[yy].substring(1));
185                                    }
186                            }
187                    }
188                }
189                resultSet.addRow(values);
190            }
191            return resultSet;
192        }
193    }