View Javadoc

1   package org.apache.torque.task;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   *   http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import java.io.File;
23  import java.util.ArrayList;
24  import java.util.Date;
25  import java.util.Hashtable;
26  import java.util.Iterator;
27  import java.util.List;
28  import java.util.Map;
29  
30  import org.apache.commons.lang.StringUtils;
31  import org.apache.tools.ant.BuildException;
32  import org.apache.tools.ant.DirectoryScanner;
33  import org.apache.tools.ant.types.FileSet;
34  import org.apache.torque.engine.EngineException;
35  import org.apache.torque.engine.database.model.Database;
36  import org.apache.velocity.VelocityContext;
37  import org.apache.velocity.context.Context;
38  import org.apache.texen.ant.TexenTask;
39  import org.kuali.core.db.torque.DatabaseParser;
40  import org.kuali.core.db.torque.KualiXmlToAppData;
41  
42  /**
43   * A base torque task that uses either a single XML schema representing a data model, or a <fileset> of XML
44   * schemas. We are making the assumption that an XML schema representing a data model contains tables for a
45   * <strong>single</strong> database.
46   * 
47   * @author <a href="mailto:jvanzyl@zenplex.com">Jason van Zyl</a>
48   * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
49   */
50  public class TorqueDataModelTask extends TexenTask {
51  	/**
52  	 * XML that describes the database model, this is transformed into the application model object.
53  	 */
54  	protected String xmlFile;
55  
56  	/** Fileset of XML schemas which represent our data models. */
57  	protected List<FileSet> filesets = new ArrayList<FileSet>();
58  
59  	/** Data models that we collect. One from each XML schema file. */
60  	protected List<Database> dataModels = new ArrayList<Database>();
61  
62  	/** Velocity context which exposes our objects in the templates. */
63  	protected Context context;
64  
65  	/**
66  	 * Map of data model name to database name. Should probably stick to the convention of them being the same but I
67  	 * know right now in a lot of cases they won't be.
68  	 */
69  	protected Hashtable<String, String> dataModelDbMap;
70  
71  	/**
72  	 * Hashtable containing the names of all the databases in our collection of schemas.
73  	 */
74  	protected Hashtable<String, String> databaseNames;
75  
76  	// !! This is probably a crappy idea having the sql file -> db map
77  	// here. I can't remember why I put it here at the moment ...
78  	// maybe I was going to map something else. It can probably
79  	// move into the SQL task.
80  
81  	/**
82  	 * Name of the properties file that maps an SQL file to a particular database.
83  	 */
84  	protected String sqldbmap;
85  
86  	/** The target database(s) we are generating SQL for. */
87  	private String targetDatabase;
88  
89  	/** Target Java package to place the generated files in. */
90  	private String targetPackage;
91  
92  	/**
93  	 * Set the sqldbmap.
94  	 * 
95  	 * @param sqldbmap
96  	 *            th db map
97  	 */
98  	public void setSqlDbMap(String sqldbmap) {
99  		// !! Make all these references files not strings.
100 		this.sqldbmap = getProject().resolveFile(sqldbmap).toString();
101 	}
102 
103 	/**
104 	 * Get the sqldbmap.
105 	 * 
106 	 * @return String sqldbmap.
107 	 */
108 	public String getSqlDbMap() {
109 		return sqldbmap;
110 	}
111 
112 	/**
113 	 * Return the data models that have been processed.
114 	 * 
115 	 * @return List data models
116 	 */
117 	public List<Database> getDataModels() {
118 		return dataModels;
119 	}
120 
121 	/**
122 	 * Return the data model to database name map.
123 	 * 
124 	 * @return Hashtable data model name to database name map.
125 	 */
126 	public Hashtable<String, String> getDataModelDbMap() {
127 		return dataModelDbMap;
128 	}
129 
130 	/**
131 	 * Get the xml schema describing the application model.
132 	 * 
133 	 * @return String xml schema file.
134 	 */
135 	public String getXmlFile() {
136 		return xmlFile;
137 	}
138 
139 	/**
140 	 * Set the xml schema describing the application model.
141 	 * 
142 	 * @param xmlFile
143 	 *            The new XmlFile value
144 	 */
145 	public void setXmlFile(String xmlFile) {
146 		this.xmlFile = xmlFile;
147 	}
148 
149 	/**
150 	 * Adds a set of xml schema files (nested fileset attribute).
151 	 * 
152 	 * @param set
153 	 *            a Set of xml schema files
154 	 */
155 	public void addFileset(FileSet set) {
156 		filesets.add(set);
157 	}
158 
159 	/**
160 	 * Get the current target database.
161 	 * 
162 	 * @return String target database(s)
163 	 */
164 	public String getTargetDatabase() {
165 		return targetDatabase;
166 	}
167 
168 	/**
169 	 * Set the current target database. (e.g. mysql, oracle, ..)
170 	 */
171 	public void setTargetDatabase(String targetDatabase) {
172 		this.targetDatabase = targetDatabase;
173 	}
174 
175 	/**
176 	 * Get the current target package.
177 	 * 
178 	 * @return return target java package.
179 	 */
180 	public String getTargetPackage() {
181 		return targetPackage;
182 	}
183 
184 	/**
185 	 * Set the current target package. This is where generated java classes will live.
186 	 */
187 	public void setTargetPackage(String targetPackage) {
188 		this.targetPackage = targetPackage;
189 	}
190 
191 	/**
192 	 * Return a SAX parser that implements the DatabaseParser interface
193 	 */
194 	protected DatabaseParser getDatabaseParser() {
195 		return new KualiXmlToAppData(getTargetDatabase(), getTargetPackage());
196 	}
197 
198 	/**
199 	 * Parse a schema XML File into a Database object
200 	 */
201 	protected Database getDataModel(File file) throws EngineException {
202 		// Get a handle to a parser
203 		DatabaseParser databaseParser = getDatabaseParser();
204 
205 		// Parse the file into a database
206 		Database database = databaseParser.parseResource(file.toString());
207 
208 		// Extract the filename
209 		database.setFileName(grokName(file.toString()));
210 
211 		// return the database
212 		return database;
213 	}
214 
215 	/**
216 	 * Get the list of schema XML files from our filesets
217 	 */
218 	protected List<File> getDataModelFiles() {
219 		// Allocate some storage
220 		List<File> dataModelFiles = new ArrayList<File>();
221 
222 		// Iterate through the filesets
223 		for (int i = 0; i < getFilesets().size(); i++) {
224 			// Extract a fileset
225 			FileSet fs = getFilesets().get(i);
226 
227 			// Create a directory scanner
228 			DirectoryScanner ds = fs.getDirectoryScanner(getProject());
229 
230 			// Figure out the directory to scan
231 			File srcDir = fs.getDir(getProject());
232 
233 			// Scan the directory
234 			String[] dataModelFilesArray = ds.getIncludedFiles();
235 
236 			// Add each file in the directory to our list
237 			for (int j = 0; j < dataModelFilesArray.length; j++) {
238 				File file = new File(srcDir, dataModelFilesArray[j]);
239 				dataModelFiles.add(file);
240 			}
241 		}
242 
243 		// Return the list of schema.xml files
244 		return dataModelFiles;
245 	}
246 
247 	/**
248 	 * Parse schema XML files into Database objects
249 	 */
250 	protected List<Database> getPopulatedDataModels() throws EngineException {
251 		// Allocate some storage
252 		List<Database> databases = new ArrayList<Database>();
253 
254 		// Only one file to parse
255 		if (getXmlFile() != null) {
256 			// Parse the file into a database object
257 			Database database = getDataModel(new File(getXmlFile()));
258 			// Add it to our list
259 			databases.add(database);
260 			// we are done
261 			return databases;
262 		}
263 
264 		// Get the list of schema XML files to parse from our filesets
265 		List<File> dataModelFiles = getDataModelFiles();
266 		// Iterate through the list, parsing each schema.xml file into a database object
267 		for (File dataModelFile : dataModelFiles) {
268 			// Parse a schema.xml file into a database object
269 			Database database = getDataModel(dataModelFile);
270 			// Add the database object to our list
271 			databases.add(database);
272 		}
273 		// Return the list of database objects
274 		return databases;
275 	}
276 
277 	/**
278 	 * Set up the initial context for generating SQL
279 	 * 
280 	 * @return the context
281 	 * @throws Exception
282 	 */
283 	public Context initControlContext() throws Exception {
284 		if (xmlFile == null && filesets.isEmpty()) {
285 			throw new BuildException("You must specify an XML schema or fileset of XML schemas!");
286 		}
287 
288 		try {
289 			dataModels = getPopulatedDataModels();
290 		} catch (EngineException ee) {
291 			throw new BuildException(ee);
292 		}
293 
294 		Iterator<Database> i = dataModels.iterator();
295 		databaseNames = new Hashtable<String, String>();
296 		dataModelDbMap = new Hashtable<String, String>();
297 
298 		// Different datamodels may state the same database
299 		// names, we just want the unique names of databases.
300 		while (i.hasNext()) {
301 			Database database = i.next();
302 			databaseNames.put(database.getName(), database.getName());
303 			dataModelDbMap.put(database.getFileName(), database.getName());
304 		}
305 
306 		context = new VelocityContext();
307 
308 		// Place our set of data models into the context along
309 		// with the names of the databases as a convenience for now.
310 		context.put("dataModels", dataModels);
311 		context.put("databaseNames", databaseNames);
312 		context.put("targetDatabase", targetDatabase);
313 		context.put("targetPackage", targetPackage);
314 
315 		return context;
316 	}
317 
318 	/**
319 	 * Change type of "now" to java.util.Date
320 	 * 
321 	 * @see org.apache.velocity.texen.ant.TexenTask#populateInitialContext(org.apache.velocity.context.Context)
322 	 */
323 	protected void populateInitialContext(Context context) throws Exception {
324 		super.populateInitialContext(context);
325 		context.put("now", new Date());
326 	}
327 
328 	/**
329 	 * Gets a name to use for the application's data model.
330 	 * 
331 	 * @param xmlFile
332 	 *            The path to the XML file housing the data model.
333 	 * @return The name to use for the <code>AppData</code>.
334 	 */
335 	private String grokName(String xmlFile) {
336 		// This can't be set from the file name as it is an unreliable
337 		// method of naming the descriptor. Not everyone uses the same
338 		// method as I do in the TDK. jvz.
339 
340 		String name = "data-model";
341 		int i = xmlFile.lastIndexOf(System.getProperty("file.separator"));
342 		if (i != -1) {
343 			// Creep forward to the start of the file name.
344 			i++;
345 
346 			int j = xmlFile.lastIndexOf('.');
347 			if (i < j) {
348 				name = xmlFile.substring(i, j);
349 			} else {
350 				// Weirdo
351 				name = xmlFile.substring(i);
352 			}
353 		}
354 		return name;
355 	}
356 
357 	/**
358 	 * Override Texen's context properties to map the torque.xxx properties (including defaults set by the
359 	 * org/apache/torque/defaults.properties) to just xxx.
360 	 * 
361 	 * <p>
362 	 * Also, move xxx.yyy properties to xxxYyy as Velocity doesn't like the xxx.yyy syntax.
363 	 * </p>
364 	 * 
365 	 * @param file
366 	 *            the file to read the properties from
367 	 */
368 	public void setContextProperties(String file) {
369 		super.setContextProperties(file);
370 
371 		// Map the torque.xxx elements from the env to the contextProperties
372 		Hashtable<?, ?> env = super.getProject().getProperties();
373 		for (Iterator<?> i = env.entrySet().iterator(); i.hasNext();) {
374 			Map.Entry<?, ?> entry = (Map.Entry<?, ?>) i.next();
375 			String key = (String) entry.getKey();
376 			if (key.startsWith("torque.")) {
377 				String newKey = key.substring("torque.".length());
378 				int j = newKey.indexOf(".");
379 				while (j != -1) {
380 					newKey = newKey.substring(0, j) + StringUtils.capitalize(newKey.substring(j + 1));
381 					j = newKey.indexOf(".");
382 				}
383 
384 				contextProperties.setProperty(newKey, entry.getValue());
385 			}
386 		}
387 	}
388 
389 	public List<FileSet> getFilesets() {
390 		return filesets;
391 	}
392 
393 	public void setFilesets(List<FileSet> filesets) {
394 		this.filesets = filesets;
395 	}
396 }