View Javadoc

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