View Javadoc

1   /**
2    * Copyright 2008 Bryan Ray
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License"); 
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at 
7    * 
8    * http://www.apache.org/licenses/LICENSE-2.0 
9    * 
10   * Unless required by applicable law or agreed to in writing, software 
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
13   * See the License for the specific language governing permissions and 
14   * limitations under the License. 
15   */
16  package uk.org.bryanray.testtoys.loader;
17  
18  import java.io.BufferedReader;
19  import java.io.File;
20  import java.io.FileInputStream;
21  import java.io.FileNotFoundException;
22  import java.io.IOException;
23  import java.io.InputStream;
24  import java.io.InputStreamReader;
25  import java.io.PrintStream;
26  import java.io.Reader;
27  import java.util.ArrayList;
28  import java.util.HashMap;
29  import java.util.Iterator;
30  import java.util.List;
31  import java.util.Map;
32  import java.util.regex.Matcher;
33  import java.util.regex.Pattern;
34  
35  import org.apache.commons.cli.CommandLine;
36  import org.apache.commons.cli.CommandLineParser;
37  import org.apache.commons.cli.GnuParser;
38  import org.apache.commons.cli.HelpFormatter;
39  import org.apache.commons.cli.Option;
40  import org.apache.commons.cli.OptionBuilder;
41  import org.apache.commons.cli.Options;
42  import org.apache.commons.cli.ParseException;
43  import org.supercsv.io.CsvMapReader;
44  import org.supercsv.prefs.CsvPreference;
45  
46  import uk.org.bryanray.testtoys.util.ExceptionUtil;
47  import uk.org.bryanray.testtoys.util.TemplateUtil;
48  
49  /**
50   * A class that generates a SQL script to load test data, from a SQL template
51   * and CSV data describing the entities.
52   */
53  public class Loader {
54  
55  	private static Map<String, ValueFormatter> FORMATTERS = new HashMap<String, ValueFormatter>();
56  	private static ValueFormatter STRING_FORMATTER = new StringFormatter();
57  
58  	static {
59  		FORMATTERS.put("st", STRING_FORMATTER);
60  		FORMATTERS.put("sq", new SqlFormatter());
61  		FORMATTERS.put("nu", new NumberFormatter());
62  	}
63  
64  	private String template = "";
65  	private String[] header = new String[0];
66  	private Iterator<Map<String, String>> testDataEntities;
67  	private Map<String, ValueFormatter> columnValueFormatters;
68  
69  	/**
70  	 * @param args
71  	 *            The command line arguments.
72  	 */
73  	public static void main(String[] args) {
74  		Loader loader = new Loader();
75  		Options commandOptions = getCommandOptions();
76  		CommandLine commandLine = parseCommandArguments(args, commandOptions);
77  		if (commandLine.hasOption("templatefile")
78  				&& commandLine.hasOption("testdatafile")
79  				&& commandLine.hasOption("sqlfile")) {
80  			String templateFileName = commandLine
81  					.getOptionValue("templatefile");
82  			String testDataFileName = commandLine
83  					.getOptionValue("testdataFile");
84  			String sqlFileName = commandLine.getOptionValue("sqlfile");
85  
86  			File templateFile = new File(templateFileName);
87  			File testDataEntitiesFile = new File(testDataFileName);
88  			File sqlOutputFile = new File(sqlFileName);
89  
90  			try {
91  				InputStream templateInputStream = new FileInputStream(
92  						templateFile);
93  				loader.setTemplate(templateInputStream);
94  			} catch (FileNotFoundException fnfe) {
95  				System.err.println("Template file not found.");
96  				return;
97  			}
98  
99  			try {
100 				InputStream testDataEntitiesInputStream = new FileInputStream(
101 						testDataEntitiesFile);
102 				loader.setTestDataEntities(testDataEntitiesInputStream);
103 			} catch (FileNotFoundException fnfe) {
104 				System.err.println("Test data entities file not found.");
105 				return;
106 			}
107 
108 			loader.configureValueFormatters();
109 
110 			try {
111 				String testData = loader.generateTestData();
112 				PrintStream output = System.out;
113 				if (null != sqlOutputFile) {
114 					output = new PrintStream(sqlOutputFile);
115 				}
116 				output.println(testData);
117 			} catch (FileNotFoundException fnfe) {
118 				System.err.println("SQL output file not found.");
119 				return;
120 			}
121 		} else {
122 			HelpFormatter formatter = new HelpFormatter();
123 			formatter.printHelp("loader", commandOptions);
124 		}
125 	}
126 
127 	/**
128 	 * @param commandArguments
129 	 *            The command arguments passed to {@link #main(String[]).
130 	 * @param options
131 	 *            The options builder from {@link #getCommandOptions().
132 	 */
133 	private static CommandLine parseCommandArguments(String[] commandArguments,
134 			Options options) {
135 		CommandLineParser parser = new GnuParser();
136 		try {
137 			return parser.parse(options, commandArguments);
138 		} catch (ParseException pe) {
139 			throw ExceptionUtil.convertException(pe);
140 		}
141 	}
142 
143 	/**
144 	 * @return A command line object that follows the builder pattern.
145 	 */
146 	@SuppressWarnings("static-access")
147 	private static Options getCommandOptions() {
148 		Options options = new Options();
149 		Option templateFile = OptionBuilder.withArgName("file").hasArg()
150 				.withDescription("use given template file").create(
151 						"templatefile");
152 		options.addOption(templateFile);
153 
154 		Option testDataFile = OptionBuilder.withArgName("file").hasArg()
155 				.withDescription("use given CSV test data file").create(
156 						"testdatafile");
157 		options.addOption(testDataFile);
158 
159 		Option sqlOutputFile = OptionBuilder.withArgName("file").hasArg()
160 				.withDescription("SQL data file to generate").create("sqlfile");
161 		options.addOption(sqlOutputFile);
162 
163 		return options;
164 	}
165 
166 	/**
167 	 * @param header
168 	 *            The header from which to remove the data types.
169 	 * @return The header without the data types.
170 	 */
171 	public String[] stripParameters(String[] header) {
172 		String[] result = new String[header.length];
173 		for (int i = 0; i < header.length; i++) {
174 			String headerItem = header[i];
175 			int p = headerItem.indexOf("(");
176 			result[i] = headerItem.substring(0, -1 < p ? p : headerItem
177 					.length());
178 		}
179 		return result;
180 	}
181 
182 	/**
183 	 * Reads the data types from the header and selects the appropriate
184 	 * formatter for the column.
185 	 */
186 	public void configureValueFormatters() {
187 		columnValueFormatters = new HashMap<String, ValueFormatter>();
188 		for (int i = 0; i < header.length; i++) {
189 			Pattern pattern = Pattern.compile("([^\\(]*)\\(([^\\)]*)\\)");//    
190 			Matcher matcher = pattern.matcher(header[i]);
191 			String name = header[i];
192 			if (matcher.matches()) {
193 				name = matcher.group(1);
194 			}
195 			if (matcher.matches()) {
196 				String type = matcher.group(2);
197 				ValueFormatter valueFormatter = FORMATTERS.get(type);
198 				columnValueFormatters.put(name, valueFormatter);
199 			} else {
200 				columnValueFormatters.put(name, STRING_FORMATTER);
201 			}
202 		}
203 	}
204 
205 	/**
206 	 * @param template
207 	 *            The SQL template.
208 	 * @param entities
209 	 *            An iterator, which supplies a {@link java.util.Map} for each
210 	 *            iteration. The map represents a single entity, with the place
211 	 *            holder value in the map value.
212 	 * @return The SQL script that will insert test data.
213 	 */
214 	public String generateTestData() {
215 		StringBuffer testData = new StringBuffer();
216 		while (testDataEntities.hasNext()) {
217 			Map<String, String> placeholderValues = testDataEntities.next();
218 
219 			// Replace all the place holder values with their formatted values.
220 			for (Iterator<Map.Entry<String, String>> i = placeholderValues
221 					.entrySet().iterator(); i.hasNext();) {
222 				Map.Entry<String, String> entry = i.next();
223 				ValueFormatter formatter = columnValueFormatters.get(entry
224 						.getKey());
225 				entry.setValue(formatter.format(entry.getValue()));
226 			}
227 
228 			// Replace all the place holders with the formatted place holder
229 			// values.
230 			testData.append(TemplateUtil.substituteTemplatePlaceholders(
231 					template, placeholderValues));
232 			testData.append("\n");
233 		}
234 		return testData.toString();
235 	}
236 
237 	/**
238 	 * An iterator, which supplies a {@link java.util.Map} for each iteration.
239 	 * The map represents a single entity, with the place holder value in the
240 	 * map value.
241 	 * 
242 	 * @param inputStream
243 	 *            The input stream to parse as CSV data.
244 	 */
245 	public void setTestDataEntities(InputStream inputStream) {
246 		List<Map<String, String>> entityList = new ArrayList<Map<String, String>>();
247 		try {
248 			Reader reader = new InputStreamReader(inputStream);
249 			CsvMapReader csvReader = new CsvMapReader(reader,
250 					CsvPreference.STANDARD_PREFERENCE);
251 			this.header = csvReader.getCSVHeader(false);
252 			String[] header = stripParameters(this.header);
253 			while (true) {
254 				Map<String, String> entity = csvReader.read(header);
255 				if (null != entity) {
256 					entityList.add(entity);
257 				} else {
258 					break;
259 				}
260 			}
261 		} catch (IOException ioe) {
262 			throw new RuntimeException(ioe);
263 		}
264 		testDataEntities = entityList.iterator();
265 	}
266 
267 	/**
268 	 * The SQL template.
269 	 * 
270 	 * @param The
271 	 *            input stream that holds the template.
272 	 */
273 	public void setTemplate(InputStream inputStream) {
274 		StringBuffer template = new StringBuffer();
275 		BufferedReader reader = new BufferedReader(new InputStreamReader(
276 				inputStream));
277 		try {
278 			template.append(reader.readLine());
279 			while (reader.ready()) {
280 				template.append(reader.readLine());
281 			}
282 			this.template = template.toString();
283 		} catch (IOException ioe) {
284 			throw new RuntimeException(ioe);
285 		}
286 	}
287 }