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