View Javadoc

1   package org.apache.torque.engine.database.model;
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.util.ArrayList;
20  import java.util.Hashtable;
21  import java.util.Iterator;
22  import java.util.List;
23  import java.util.Properties;
24  
25  import org.apache.torque.engine.EngineException;
26  
27  import org.xml.sax.Attributes;
28  
29  
30  /***
31   * A class for holding application data structures.
32   *
33   * @author <a href="mailto:leon@opticode.co.za>Leon Messerschmidt</a>
34   * @author <a href="mailto:jmcnally@collab.net>John McNally</a>
35   * @author <a href="mailto:mpoeschl@marmot.at>Martin Poeschl</a>
36   * @author <a href="mailto:dlr@collab.net>Daniel Rall</a>
37   * @author <a href="mailto:byron_foster@byron_foster@yahoo.com>Byron Foster</a>
38   * @version $Id: Database.java,v 1.6.2.2 2004/05/20 04:34:15 seade Exp $
39   */
40  public class Database
41  {
42      private String databaseType = null;
43      private List tableList = new ArrayList(100);
44      private String name;
45      private String pkg;
46      private String baseClass;
47      private String basePeer;
48      private String defaultIdMethod;
49      private String defaultJavaType;
50      private String defaultJavaNamingMethod;
51      private AppData dbParent;
52      private Hashtable tablesByName = new Hashtable();
53      private Hashtable tablesByJavaName = new Hashtable();
54      private boolean heavyIndexing;
55  
56      /***
57       * Load the database object from an xml tag.
58       *
59       * @param attrib the xml attributes
60       */
61      public void loadFromXML(Attributes attrib)
62      {
63          setName(attrib.getValue("name"));
64          pkg = attrib.getValue("package");
65          baseClass = attrib.getValue("baseClass");
66          basePeer = attrib.getValue("basePeer");
67          defaultJavaType = attrib.getValue("defaultJavaType");
68          defaultIdMethod = attrib.getValue("defaultIdMethod");
69          defaultJavaNamingMethod = attrib.getValue("defaultJavaNamingMethod");
70          if (defaultJavaNamingMethod == null)
71          {
72              defaultJavaNamingMethod = NameGenerator.CONV_METHOD_UNDERSCORE;
73          }
74          heavyIndexing = "true".equals(attrib.getValue("heavyIndexing"));
75      }
76  
77      /***
78       * Get the name of the Database
79       *
80       * @return name of the Database
81       */
82      public String getName()
83      {
84          return name;
85      }
86  
87      /***
88       * Set the name of the Database
89       *
90       * @param name name of the Database
91       */
92      public void setName(String name)
93      {
94          /*** @task check this */
95  //        this.name = (name == null ? Torque.getDefaultDB() : name);
96          this.name = (name == null ? "default" : name);
97      }
98  
99      /***
100      * Get the value of package.
101      * @return value of package.
102      */
103     public String getPackage()
104     {
105         return pkg;
106     }
107 
108     /***
109      * Set the value of package.
110      * @param v  Value to assign to package.
111      */
112     public void setPackage(String v)
113     {
114         this.pkg = v;
115     }
116 
117     /***
118      * Get the value of baseClass.
119      * @return value of baseClass.
120      */
121     public String getBaseClass()
122     {
123         if (baseClass == null)
124         {
125             return "BaseObject";
126         }
127         return baseClass;
128     }
129 
130     /***
131      * Set the value of baseClass.
132      * @param v  Value to assign to baseClass.
133      */
134     public void setBaseClass(String v)
135     {
136         this.baseClass = v;
137     }
138 
139     /***
140      * Get the value of basePeer.
141      * @return value of basePeer.
142      */
143     public String getBasePeer()
144     {
145         if (basePeer == null)
146         {
147             return "BasePeer";
148         }
149         return basePeer;
150     }
151 
152     /***
153      * Set the value of basePeer.
154      * @param v Value to assign to basePeer.
155      */
156     public void setBasePeer(String v)
157     {
158         this.basePeer = v;
159     }
160 
161     /***
162      * Get the value of defaultIdMethod.
163      * @return value of defaultIdMethod.
164      */
165     public String getDefaultIdMethod()
166     {
167         return defaultIdMethod;
168     }
169 
170     /***
171      * Set the value of defaultIdMethod.
172      * @param v Value to assign to defaultIdMethod.
173      */
174     public void setDefaultIdMethod(String v)
175     {
176         this.defaultIdMethod = v;
177     }
178 
179     /***
180      * Get type to use in Java sources (primitive || object)
181      *
182      * @return the type to use
183      */
184     public String getDefaultJavaType()
185     {
186         return defaultJavaType;
187     }
188 
189     /***
190      * Get the value of defaultJavaNamingMethod which specifies the
191      * method for converting schema names for table and column to Java names.
192      *
193      * @return The default naming conversion used by this database.
194      */
195     public String getDefaultJavaNamingMethod()
196     {
197         return defaultJavaNamingMethod;
198     }
199 
200     /***
201      * Set the value of defaultJavaNamingMethod.
202      * @param v The default naming conversion for this database to use.
203      */
204     public void setDefaultJavaNamingMethod(String v)
205     {
206         this.defaultJavaNamingMethod = v;
207     }
208 
209     /***
210      * Get the value of heavyIndexing.
211      * @return value of heavyIndexing.
212      */
213     public boolean isHeavyIndexing()
214     {
215         return heavyIndexing;
216     }
217 
218     /***
219      * Set the value of heavyIndexing.
220      * @param v  Value to assign to heavyIndexing.
221      */
222     public void setHeavyIndexing(boolean v)
223     {
224         this.heavyIndexing = v;
225     }
226 
227     /***
228      * Return an array of all tables
229      *
230      * @return array of all tables
231      */
232     public Table[] getTables()
233     {
234         int size = tableList.size();
235         Table[] tbls = new Table[size];
236         for (int i = 0; i < size; i++)
237         {
238             tbls[i] = (Table) tableList.get(i);
239         }
240         return tbls;
241     }
242 
243     /***
244      * Return the table with the specified name.
245      *
246      * @param name table name
247      * @return A Table object.  If it does not exist it returns null
248      */
249     public Table getTable(String name)
250     {
251         return (Table) tablesByName.get(name);
252     }
253 
254     /***
255      * Return the table with the specified javaName.
256      *
257      * @param javaName name of the java object representing the table
258      * @return A Table object.  If it does not exist it returns null
259      */
260     public Table getTableByJavaName(String javaName)
261     {
262         return (Table) tablesByJavaName.get(javaName);
263     }
264 
265     /***
266      * An utility method to add a new table from an xml attribute.
267      *
268      * @param attrib the xml attributes
269      * @return the created Table
270      */
271     public Table addTable(Attributes attrib)
272     {
273         Table tbl = new Table();
274         tbl.setDatabase(this);
275         tbl.loadFromXML(attrib, this.getDefaultIdMethod());
276         addTable(tbl);
277         return tbl;
278     }
279 
280     /***
281      * Add a table to the list and sets the Database property to this Database
282      *
283      * @param tbl the table to add
284      */
285     public void addTable(Table tbl)
286     {
287         tbl.setDatabase(this);
288         tableList.add(tbl);
289         tablesByName.put(tbl.getName(), tbl);
290         tablesByJavaName.put(tbl.getJavaName(), tbl);
291         tbl.setPackage(getPackage());
292     }
293 
294     /***
295      * Set the parent of the database
296      *
297      * @param parent the parent
298      */
299     public void setAppData(AppData parent)
300     {
301         dbParent = parent;
302     }
303 
304     /***
305      * Get the parent of the table
306      *
307      * @return the parent
308      */
309     public AppData getAppData()
310     {
311         return dbParent;
312     }
313 
314     protected String getDatabaseType()
315     {
316         return databaseType;
317     }
318 
319     public void setDatabaseType(String databaseType)
320     {
321         this.databaseType = databaseType;
322     }
323 
324     /***
325      * Returns the value of the named property from this database's
326      * <code>db.props</code> file.
327      *
328      * @param name The name of the property to retrieve the value of.
329      * @return The value of the specified property.
330      * @exception EngineException Couldn't access properties.
331      */
332     protected String getProperty(String name)
333         throws EngineException
334     {
335         Properties p = getAppData().getIdiosyncrasies(databaseType);
336         return (p == null ? null : p.getProperty(name));
337     }
338 
339     /***
340      * Determines if this database will be using the
341      * <code>IDMethod.ID_BROKER</code> to create ids for torque OM
342      * objects.
343      * @return true if there is at least one table in this database that
344      * uses the <code>IDMethod.ID_BROKER</code> method of generating
345      * ids. returns false otherwise.
346      */
347     public boolean requiresIdTable()
348     {
349         Table table[] = getTables();
350         for (int i = 0; i < table.length; i++)
351         {
352             if (table[i].getIdMethod().equals(IDMethod.ID_BROKER))
353             {
354                 return true;
355             }
356         }
357         return false;
358     }
359 
360     public void doFinalInitialization()
361             throws EngineException
362     {
363         Table[] tables = getTables();
364         for (int i = 0; i < tables.length; i++)
365         {
366             Table currTable = tables[i];
367 
368             // check schema integrity
369             // if idMethod="autoincrement", make sure a column is
370             // specified as autoIncrement="true"
371             // FIXME: Handle idMethod="native" via DB adapter.
372             if (currTable.getIdMethod().equals("autoincrement"))
373             {
374                 Column[] columns = currTable.getColumns();
375                 boolean foundOne = false;
376                 for (int j = 0; j < columns.length && !foundOne; j++)
377                 {
378                     foundOne = columns[j].isAutoIncrement();
379                 }
380 
381                 if (!foundOne)
382                 {
383                     String errorMessage = "Table '" + currTable.getName()
384                             + "' is marked as autoincrement, but it does not "
385                             + "have a column which declared as the one to "
386                             + "auto increment (i.e. autoIncrement=\"true\")\n";
387                     throw new EngineException("Error in XML schema: " + errorMessage);
388                 }
389             }
390 
391             currTable.doFinalInitialization();
392 
393             // setup reverse fk relations
394             ForeignKey[] fks = currTable.getForeignKeys();
395             for (int j = 0; j < fks.length; j++)
396             {
397                 ForeignKey currFK = fks[j];
398                 Table foreignTable = getTable(currFK.getForeignTableName());
399                 if (foreignTable == null)
400                 {
401                     throw new EngineException("Attempt to set foreign"
402                             + " key to nonexistent table, "
403                             + currFK.getForeignTableName());
404                 }
405                 else
406                 {
407                     List referrers = foreignTable.getReferrers();
408                     if ((referrers == null || !referrers.contains(currFK)))
409                     {
410                         foreignTable.addReferrer(currFK);
411                     }
412 
413                     // local column references
414                     Iterator localColumnNames = currFK.getLocalColumns().iterator();
415                     while (localColumnNames.hasNext())
416                     {
417                         Column local = currTable
418                                 .getColumn((String) localColumnNames.next());
419                         // give notice of a schema inconsistency.
420                         // note we do not prevent the npe as there is nothing
421                         // that we can do, if it is to occur.
422                         if (local == null)
423                         {
424                             throw new EngineException("Attempt to define foreign"
425                                     + " key with nonexistent column in table, "
426                                     + currTable.getName());
427                         }
428                         else
429                         {
430                             //check for foreign pk's
431                             if (local.isPrimaryKey())
432                             {
433                                 currTable.setContainsForeignPK(true);
434                             }
435                         }
436                     }
437 
438                     // foreign column references
439                     Iterator foreignColumnNames
440                             = currFK.getForeignColumns().iterator();
441                     while (foreignColumnNames.hasNext())
442                     {
443                         String foreignColumnName = (String) foreignColumnNames.next();
444                         Column foreign = foreignTable.getColumn(foreignColumnName);
445                         // if the foreign column does not exist, we may have an
446                         // external reference or a misspelling
447                         if (foreign == null)
448                         {
449                             throw new EngineException("Attempt to set foreign"
450                                     + " key to nonexistent column: table="
451                                     +  currTable.getName() + ", foreign column=" 
452                                     +  foreignColumnName);
453                         }
454                         else
455                         {
456                             foreign.addReferrer(currFK);
457                         }
458                     }
459                 }
460             }
461         }
462     }
463 
464     /***
465      * Creats a string representation of this Database.
466      * The representation is given in xml format.
467      *
468      * @return string representation in xml
469      */
470     public String toString()
471     {
472         StringBuffer result = new StringBuffer();
473 
474         result.append("<database name=\"").append(getName()).append('"')
475             .append(" package=\"").append(getPackage()).append('"')
476             .append(" defaultIdMethod=\"").append(getDefaultIdMethod())
477             .append('"')
478             .append(" baseClass=\"").append(getBaseClass()).append('"')
479             .append(" basePeer=\"").append(getBasePeer()).append('"')
480             .append(">\n");
481 
482         for (Iterator i = tableList.iterator(); i.hasNext();)
483         {
484             result.append(i.next());
485         }
486 
487         result.append("</database>");
488         return result.toString();
489     }
490 }