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.torque.engine.database.transform.XmlToAppData;
37  import org.apache.velocity.VelocityContext;
38  import org.apache.velocity.context.Context;
39  import org.apache.velocity.texen.ant.TexenTask;
40  
41  /***
42   * A base torque task that uses either a single XML schema
43   * representing a data model, or a <fileset> of XML schemas.
44   * We are making the assumption that an XML schema representing
45   * a data model contains tables for a <strong>single</strong>
46   * database.
47   *
48   * @author <a href="mailto:jvanzyl@zenplex.com">Jason van Zyl</a>
49   * @author <a href="mailto:dlr@finemaltcoding.com">Daniel Rall</a>
50   */
51  public class TorqueDataModelTask extends TexenTask
52  {
53      /***
54       * XML that describes the database model, this is transformed
55       * into the application model object.
56       */
57      protected String xmlFile;
58  
59      /*** Fileset of XML schemas which represent our data models. */
60      protected List filesets = new ArrayList();
61  
62      /*** Data models that we collect. One from each XML schema file. */
63      protected List dataModels = new ArrayList();
64  
65      /*** Velocity context which exposes our objects in the templates. */
66      protected Context context;
67  
68      /***
69       * Map of data model name to database name.
70       * Should probably stick to the convention of them being the same but
71       * I know right now in a lot of cases they won't be.
72       */
73      protected Hashtable dataModelDbMap;
74  
75      /***
76       * Hashtable containing the names of all the databases
77       * in our collection of schemas.
78       */
79      protected Hashtable databaseNames;
80  
81      //!! This is probably a crappy idea having the sql file -> db map
82      // here. I can't remember why I put it here at the moment ...
83      // maybe I was going to map something else. It can probably
84      // move into the SQL task.
85  
86      /***
87       * Name of the properties file that maps an SQL file
88       * to a particular database.
89       */
90      protected String sqldbmap;
91  
92      /*** The target database(s) we are generating SQL for. */
93      private String targetDatabase;
94  
95      /**</package-summary/html">Target Java package to place the generated files in/ *//package-summary.html">em>* Target Java package to place the generated files in. */
96      private String targetPackage;
97  
98  
99      /***
100      * Set the sqldbmap.
101      *
102      * @param sqldbmap th db map
103      */
104     public void setSqlDbMap(String sqldbmap)
105     {
106         //!! Make all these references files not strings.
107         this.sqldbmap = getProject().resolveFile(sqldbmap).toString();
108     }
109 
110     /***
111      * Get the sqldbmap.
112      *
113      * @return String sqldbmap.
114      */
115     public String getSqlDbMap()
116     {
117         return sqldbmap;
118     }
119 
120     /***
121      * Return the data models that have been processed.
122      *
123      * @return List data models
124      */
125     public List getDataModels()
126     {
127         return dataModels;
128     }
129 
130     /***
131      * Return the data model to database name map.
132      *
133      * @return Hashtable data model name to database name map.
134      */
135     public Hashtable getDataModelDbMap()
136     {
137         return dataModelDbMap;
138     }
139 
140     /***
141      * Get the xml schema describing the application model.
142      *
143      * @return  String xml schema file.
144      */
145     public String getXmlFile()
146     {
147         return xmlFile;
148     }
149 
150     /***
151      * Set the xml schema describing the application model.
152      *
153      * @param xmlFile The new XmlFile value
154      */
155     public void setXmlFile(String xmlFile)
156     {
157         this.xmlFile = getProject().resolveFile(xmlFile).toString();
158     }
159 
160     /***
161      * Adds a set of xml schema files (nested fileset attribute).
162      *
163      * @param set a Set of xml schema files
164      */
165     public void addFileset(FileSet set)
166     {
167         filesets.add(set);
168     }
169 
170     /***
171      * Get the current target database.
172      *
173      * @return String target database(s)
174      */
175     public String getTargetDatabase()
176     {
177         return targetDatabase;
178     }
179 
180     /***
181      * Set the current target database. (e.g. mysql, oracle, ..)
182      *
183      * @param v target database(s)
184      */
185     public void setTargetDatabase(String v)
186     {
187         targetDatabase = v;
188     }
189 
190     /***
191      * Get the current target package.
192      *
193      * @return return target java package.
194      */
195     public String getTargetPackage()
196     {
197         return targetPackage;
198     }
199 
200     /***
201      * Set the current target package. This is where generated java classes will
202      * live.
203      *
204      * @param v target java package.
205      */
206     public void setTargetPackage(String v)
207     {
208         targetPackage = v;
209     }
210 
211     /***
212      * Set up the initial context for generating the SQL from the XML schema.
213      *
214      * @return the context
215      * @throws Exception
216      */
217     public Context initControlContext() throws Exception
218     {
219         XmlToAppData xmlParser;
220 
221         if (xmlFile == null && filesets.isEmpty())
222         {
223             throw new BuildException("You must specify an XML schema or "
224                     + "fileset of XML schemas!");
225         }
226 
227         try
228         {
229             if (xmlFile != null)
230             {
231                 // Transform the XML database schema into
232                 // data model object.
233                 xmlParser = new XmlToAppData(getTargetDatabase(),
234                         getTargetPackage());
235                 Database ad = xmlParser.parseFile(xmlFile);
236                 ad.setFileName(grokName(xmlFile));
237                 dataModels.add(ad);
238             }
239             else
240             {
241                 // Deal with the filesets.
242                 for (int i = 0; i < filesets.size(); i++)
243                 {
244                     FileSet fs = (FileSet) filesets.get(i);
245                     DirectoryScanner ds = fs.getDirectoryScanner(getProject());
246                     File srcDir = fs.getDir(getProject());
247 
248                     String[] dataModelFiles = ds.getIncludedFiles();
249 
250                     // Make a transaction for each file
251                     for (int j = 0; j < dataModelFiles.length; j++)
252                     {
253                         File f = new File(srcDir, dataModelFiles[j]);
254                         xmlParser = new XmlToAppData(getTargetDatabase(),
255                                 getTargetPackage());
256                         Database ad = xmlParser.parseFile(f.toString());
257                         ad.setFileName(grokName(f.toString()));
258                         dataModels.add(ad);
259                     }
260                 }
261             }
262 
263             Iterator i = dataModels.iterator();
264             databaseNames = new Hashtable();
265             dataModelDbMap = new Hashtable();
266 
267             // Different datamodels may state the same database
268             // names, we just want the unique names of databases.
269             while (i.hasNext())
270             {
271                 Database database = (Database) i.next();
272                 databaseNames.put(database.getName(), database.getName());
273                 dataModelDbMap.put(database.getFileName(), database.getName());
274             }
275         }
276         catch (EngineException ee)
277         {
278             throw new BuildException(ee);
279         }
280 
281         context = new VelocityContext();
282 
283         // Place our set of data models into the context along
284         // with the names of the databases as a convenience for now.
285         context.put("dataModels", dataModels);
286         context.put("databaseNames", databaseNames);
287         context.put("targetDatabase", targetDatabase);
288         context.put("targetPackage", targetPackage);
289 
290         return context;
291     }
292 
293     /***
294      * Change type of "now" to java.util.Date
295      *
296      * @see org.apache.velocity.texen.ant.TexenTask#populateInitialContext(org.apache.velocity.context.Context)
297      */
298     protected void populateInitialContext(Context context) throws Exception
299     {
300         super.populateInitialContext(context);
301         context.put("now", new Date());
302     }
303 
304     /***
305      * Gets a name to use for the application's data model.
306      *
307      * @param xmlFile The path to the XML file housing the data model.
308      * @return The name to use for the <code>AppData</code>.
309      */
310     private String grokName(String xmlFile)
311     {
312         // This can't be set from the file name as it is an unreliable
313         // method of naming the descriptor. Not everyone uses the same
314         // method as I do in the TDK. jvz.
315 
316         String name = "data-model";
317         int i = xmlFile.lastIndexOf(System.getProperty("file.separator"));
318         if (i != -1)
319         {
320             // Creep forward to the start of the file name.
321             i++;
322 
323             int j = xmlFile.lastIndexOf('.');
324             if (i < j)
325             {
326                 name = xmlFile.substring(i, j);
327             }
328             else
329             {
330                 // Weirdo
331                 name = xmlFile.substring(i);
332             }
333         }
334         return name;
335     }
336 
337     /***
338      * Override Texen's context properties to map the
339      * torque.xxx properties (including defaults set by the
340      * org/apache/torque/defaults.properties) to just xxx.
341      *
342      * <p>
343      * Also, move xxx.yyy properties to xxxYyy as Velocity
344      * doesn't like the xxx.yyy syntax.
345      * </p>
346      *
347      * @param file the file to read the properties from
348      */
349     public void setContextProperties(String file)
350     {
351         super.setContextProperties(file);
352 
353         // Map the torque.xxx elements from the env to the contextProperties
354         Hashtable env = super.getProject().getProperties();
355         for (Iterator i = env.entrySet().iterator(); i.hasNext();)
356         {
357             Map.Entry entry = (Map.Entry) i.next();
358             String key = (String) entry.getKey();
359             if (key.startsWith("torque."))
360             {
361                 String newKey = key.substring("torque.".length());
362                 int j = newKey.indexOf(".");
363                 while (j != -1)
364                 {
365                     newKey =
366                         newKey.substring(0, j)
367                         +  StringUtils.capitalize(newKey.substring(j + 1));
368                     j = newKey.indexOf(".");
369                 }
370 
371                 contextProperties.setProperty(newKey, entry.getValue());
372             }
373         }
374     }
375 }