View Javadoc

1   package org.apache.torque.engine.database.model;
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.util.ArrayList;
17  import java.util.Collections;
18  import java.util.HashMap;
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.collections.map.ListOrderedMap;
25  import org.apache.commons.logging.Log;
26  import org.apache.commons.logging.LogFactory;
27  import org.apache.torque.engine.EngineException;
28  import org.apache.torque.engine.database.transform.DTDResolver;
29  import org.apache.torque.engine.platform.Platform;
30  import org.apache.torque.engine.platform.PlatformFactory;
31  import org.xml.sax.Attributes;
32  
33  /**
34   * A class for holding application data structures.
35   * 
36   * @author <a href="mailto:leon@opticode.co.za>Leon Messerschmidt</a>
37   * @author <a href="mailto:jmcnally@collab.net>John McNally</a>
38   * @author <a href="mailto:mpoeschl@marmot.at>Martin Poeschl</a>
39   * @author <a href="mailto:dlr@collab.net>Daniel Rall</a>
40   * @author <a href="mailto:byron_foster@byron_foster@yahoo.com>Byron Foster</a>
41   * @author <a href="mailto:monroe@dukece.com>Greg Monroe</a>
42   * @version $Id: Database.java,v 1.1.6.1 2008-04-01 04:07:48 jkeller Exp $
43   */
44  public class Database {
45      /** Logging class from commons.logging */
46      private static Log log = LogFactory.getLog(Database.class);
47  
48      private String databaseType = null;
49      private List tableList = new ArrayList(100);
50      private Map domainMap = new HashMap();
51      private String name;
52      private String javaName;
53      private String pkg;
54      private String baseClass;
55      private String basePeer;
56      private String defaultIdMethod;
57      private String defaultJavaType;
58      private String defaultJavaNamingMethod;
59      private Hashtable tablesByName = new Hashtable();
60      private Hashtable tablesByJavaName = new Hashtable();
61      private boolean heavyIndexing;
62      /** the name of the definition file */
63      private String fileName;
64      private Map options = Collections.synchronizedMap(new ListOrderedMap());
65  
66      /**
67       * Creates a new instance for the specified database type.
68       * 
69       * @param databaseType
70       *            The default type for this database.
71       */
72      public Database(String databaseType) {
73          this.databaseType = databaseType;
74      }
75  
76      /**
77       * Load the database object from an xml tag.
78       * 
79       * @param attrib
80       *            the xml attributes
81       */
82      public void loadFromXML(Attributes attrib) {
83          setName(attrib.getValue("name"));
84          pkg = attrib.getValue("package");
85          baseClass = attrib.getValue("baseClass");
86          basePeer = attrib.getValue("basePeer");
87          defaultJavaType = attrib.getValue("defaultJavaType");
88          defaultIdMethod = attrib.getValue("defaultIdMethod");
89          defaultJavaNamingMethod = attrib.getValue("defaultJavaNamingMethod");
90          if (defaultJavaNamingMethod == null) {
91              defaultJavaNamingMethod = NameGenerator.CONV_METHOD_UNDERSCORE;
92          }
93          heavyIndexing = "true".equals(attrib.getValue("heavyIndexing"));
94      }
95  
96      /**
97       * Get the name of the Database
98       * 
99       * @return name of the Database
100      */
101     public String getName() {
102         return name;
103     }
104 
105     /**
106      * Set the name of the Database
107      * 
108      * @param name
109      *            name of the Database
110      */
111     public void setName(String name) {
112         /** @task check this */
113         // this.name = (name == null ? Torque.getDefaultDB() : name);
114         this.name = (name == null ? "default" : name);
115     }
116 
117     public String getFileName() {
118         return fileName;
119     }
120 
121     public void setFileName(String name) {
122         this.fileName = name;
123     }
124 
125     /**
126      * Get the value of package.
127      * 
128      * @return value of package.
129      */
130     public String getPackage() {
131         return pkg;
132     }
133 
134     /**
135      * Set the value of package.
136      * 
137      * @param v
138      *            Value to assign to package.
139      */
140     public void setPackage(String v) {
141         this.pkg = v;
142     }
143 
144     /**
145      * Get the value of baseClass.
146      * 
147      * @return value of baseClass.
148      */
149     public String getBaseClass() {
150         if (baseClass == null) {
151             return "BaseObject";
152         }
153         return baseClass;
154     }
155 
156     /**
157      * Set the value of baseClass.
158      * 
159      * @param v
160      *            Value to assign to baseClass.
161      */
162     public void setBaseClass(String v) {
163         this.baseClass = v;
164     }
165 
166     /**
167      * Get the value of basePeer.
168      * 
169      * @return value of basePeer.
170      */
171     public String getBasePeer() {
172         if (basePeer == null) {
173             return "BasePeer";
174         }
175         return basePeer;
176     }
177 
178     /**
179      * Set the value of basePeer.
180      * 
181      * @param v
182      *            Value to assign to basePeer.
183      */
184     public void setBasePeer(String v) {
185         this.basePeer = v;
186     }
187 
188     /**
189      * Get the value of defaultIdMethod.
190      * 
191      * @return value of defaultIdMethod.
192      */
193     public String getDefaultIdMethod() {
194         return defaultIdMethod;
195     }
196 
197     /**
198      * Set the value of defaultIdMethod.
199      * 
200      * @param v
201      *            Value to assign to defaultIdMethod.
202      */
203     public void setDefaultIdMethod(String v) {
204         this.defaultIdMethod = v;
205     }
206 
207     /**
208      * Get type to use in Java sources (primitive || object)
209      * 
210      * @return the type to use
211      */
212     public String getDefaultJavaType() {
213         return defaultJavaType;
214     }
215 
216     /**
217      * Get the value of defaultJavaNamingMethod which specifies the method for converting schema names for table and
218      * column to Java names.
219      * 
220      * @return The default naming conversion used by this database.
221      */
222     public String getDefaultJavaNamingMethod() {
223         return defaultJavaNamingMethod;
224     }
225 
226     /**
227      * Set the value of defaultJavaNamingMethod.
228      * 
229      * @param v
230      *            The default naming conversion for this database to use.
231      */
232     public void setDefaultJavaNamingMethod(String v) {
233         this.defaultJavaNamingMethod = v;
234     }
235 
236     /**
237      * Get the value of heavyIndexing.
238      * 
239      * @return value of heavyIndexing.
240      */
241     public boolean isHeavyIndexing() {
242         return heavyIndexing;
243     }
244 
245     /**
246      * Set the value of heavyIndexing.
247      * 
248      * @param v
249      *            Value to assign to heavyIndexing.
250      */
251     public void setHeavyIndexing(boolean v) {
252         this.heavyIndexing = v;
253     }
254 
255     /**
256      * Return an List of all tables
257      * 
258      * @return List of all tables
259      */
260     public List getTables() {
261         return tableList;
262     }
263 
264     /**
265      * Return the table with the specified name.
266      * 
267      * @param name
268      *            table name
269      * @return A Table object. If it does not exist it returns null
270      */
271     public Table getTable(String name) {
272         return (Table) tablesByName.get(name);
273     }
274 
275     /**
276      * Return the table with the specified javaName.
277      * 
278      * @param javaName
279      *            name of the java object representing the table
280      * @return A Table object. If it does not exist it returns null
281      */
282     public Table getTableByJavaName(String javaName) {
283         return (Table) tablesByJavaName.get(javaName);
284     }
285 
286     /**
287      * An utility method to add a new table from an xml attribute.
288      * 
289      * @param attrib
290      *            the xml attributes
291      * @return the created Table
292      */
293     public Table addTable(Attributes attrib) {
294         Table tbl = new Table();
295         tbl.setDatabase(this);
296         tbl.loadFromXML(attrib, this.getDefaultIdMethod());
297         addTable(tbl);
298         return tbl;
299     }
300 
301     /**
302      * Add a table to the list and sets the Database property to this Database
303      * 
304      * @param tbl
305      *            the table to add
306      */
307     public void addTable(Table tbl) {
308         tbl.setDatabase(this);
309         tbl.setPackage(getPackage());
310         tableList.add(tbl);
311         List<String> tableNames = getTableNames(tbl);
312         for (String tableName : tableNames) {
313             tablesByName.put(tableName, tbl);
314         }
315         tablesByJavaName.put(tbl.getJavaName(), tbl);
316     }
317 
318     /**
319      * Remove a table from this database
320      */
321     public void removeTable(Table tbl) {
322         removeFromList(tbl);
323         List<String> tableNames = getTableNames(tbl);
324         for (String tableName : tableNames) {
325             tablesByName.remove(tableName);
326         }
327         tablesByJavaName.remove(tbl.getJavaName());
328     }
329 
330     protected List<String> getTableNames(Table table) {
331         List<String> tableNames = new ArrayList<String>();
332         tableNames.add(table.getName());
333         tableNames.add(table.getName().toLowerCase());
334         tableNames.add(table.getName().toUpperCase());
335         return tableNames;
336     }
337 
338     protected void removeFromList(Table targetTable) {
339         for (int i = 0; i < tableList.size(); i++) {
340             Table table = (Table) tableList.get(i);
341             String name1 = table.getName();
342             String name2 = targetTable.getName();
343             if (name1.equalsIgnoreCase(name2)) {
344                 tableList.remove(i);
345                 return;
346             }
347         }
348 
349     }
350 
351     public void addDomain(Domain domain) {
352         domainMap.put(domain.getName(), domain);
353     }
354 
355     public Domain getDomain(String domainName) {
356         return (Domain) domainMap.get(domainName);
357     }
358 
359     protected String getDatabaseType() {
360         return databaseType;
361     }
362 
363     public void setDatabaseType(String databaseType) {
364         this.databaseType = databaseType;
365     }
366 
367     /**
368      * Returns the Platform implementation for this database.
369      * 
370      * @return a Platform implementation
371      */
372     public Platform getPlatform() {
373         return PlatformFactory.getPlatformFor(databaseType);
374     }
375 
376     /**
377      * Determines if this database will be using the <code>IDMethod.ID_BROKER</code> to create ids for torque OM
378      * objects.
379      * 
380      * @return true if there is at least one table in this database that uses the <code>IDMethod.ID_BROKER</code> method
381      *         of generating ids. returns false otherwise.
382      */
383     public boolean requiresIdTable() {
384         Iterator iter = getTables().iterator();
385         while (iter.hasNext()) {
386             Table table = (Table) iter.next();
387             if (table.getIdMethod().equals(IDMethod.ID_BROKER)) {
388                 return true;
389             }
390         }
391         return false;
392     }
393 
394     /**
395      * Initializes the model.
396      * 
397      * @throws EngineException
398      */
399     public void doFinalInitialization() throws EngineException {
400         Iterator iter = getTables().iterator();
401         while (iter.hasNext()) {
402             Table currTable = (Table) iter.next();
403 
404             // check schema integrity
405             // if idMethod="autoincrement", make sure a column is
406             // specified as autoIncrement="true"
407             // FIXME: Handle idMethod="native" via DB adapter.
408             // TODO autoincrement is no longer supported!!!
409             if (currTable.getIdMethod().equals("autoincrement")) {
410                 boolean foundOne = false;
411                 Iterator colIter = currTable.getColumns().iterator();
412                 while (colIter.hasNext() && !foundOne) {
413                     foundOne = ((Column) colIter.next()).isAutoIncrement();
414                 }
415 
416                 if (!foundOne) {
417                     String errorMessage = "Table '" + currTable.getName()
418                             + "' is marked as autoincrement, but it does not "
419                             + "have a column which declared as the one to "
420                             + "auto increment (i.e. autoIncrement=\"true\")\n";
421                     throw new EngineException("Error in XML schema: " + errorMessage);
422                 }
423             }
424 
425             currTable.doFinalInitialization();
426 
427             // setup reverse fk relations
428             Iterator fks = currTable.getForeignKeys().iterator();
429             while (fks.hasNext()) {
430                 ForeignKey currFK = (ForeignKey) fks.next();
431                 Table foreignTable = getTable(currFK.getForeignTableName());
432                 if (foreignTable == null) {
433                     throw new EngineException("Attempt to set foreign" + " key to nonexistent table, "
434                             + currFK.getForeignTableName());
435                 } else {
436                     // TODO check type and size
437                     List referrers = foreignTable.getReferrers();
438                     if ((referrers == null || !referrers.contains(currFK))) {
439                         foreignTable.addReferrer(currFK);
440                     }
441 
442                     // local column references
443                     Iterator localColumnNames = currFK.getLocalColumns().iterator();
444                     while (localColumnNames.hasNext()) {
445                         Column local = currTable.getColumn((String) localColumnNames.next());
446                         // give notice of a schema inconsistency.
447                         // note we do not prevent the npe as there is nothing
448                         // that we can do, if it is to occur.
449                         if (local == null) {
450                             throw new EngineException("Attempt to define foreign"
451                                     + " key with nonexistent column in table, " + currTable.getName());
452                         } else {
453                             // check for foreign pk's
454                             if (local.isPrimaryKey()) {
455                                 currTable.setContainsForeignPK(true);
456                             }
457                         }
458                     }
459 
460                     // foreign column references
461                     Iterator foreignColumnNames = currFK.getForeignColumns().iterator();
462                     while (foreignColumnNames.hasNext()) {
463                         String foreignColumnName = (String) foreignColumnNames.next();
464                         Column foreign = foreignTable.getColumn(foreignColumnName);
465                         // if the foreign column does not exist, we may have an
466                         // external reference or a misspelling
467                         if (foreign == null) {
468                             throw new EngineException("Attempt to set foreign" + " key to nonexistent column: table="
469                                     + currTable.getName() + ", foreign column=" + foreignColumnName);
470                         } else {
471                             foreign.addReferrer(currFK);
472                         }
473                     }
474                 }
475             }
476         }
477     }
478 
479     /**
480      * Get the base name to use when creating related Java Classes.
481      * 
482      * @return A Java syntax capatible version of the dbName using the method defined by the defaultJavaNamingMethod XML
483      *         value.
484      */
485     public String getJavaName() {
486         if (javaName == null) {
487             List inputs = new ArrayList(2);
488             inputs.add(name);
489             inputs.add(defaultJavaNamingMethod);
490             try {
491                 javaName = NameFactory.generateName(NameFactory.JAVA_GENERATOR, inputs);
492             } catch (EngineException e) {
493                 log.error(e, e);
494             }
495         }
496         return javaName;
497     }
498 
499     /**
500      * Convert dbName to a Java compatible name by the JavaName method only (ignores the defaultJavaNamingMethod).
501      * 
502      * @return The current dbName converted to a standard format that can be used as part of a Java Object name.
503      */
504     public String getStandardJavaName() {
505         if (javaName == null) {
506             List inputs = new ArrayList(2);
507             inputs.add(name);
508             inputs.add(NameGenerator.CONV_METHOD_JAVANAME);
509             try {
510                 javaName = NameFactory.generateName(NameFactory.JAVA_GENERATOR, inputs);
511             } catch (EngineException e) {
512                 log.error(e, e);
513             }
514         }
515         return javaName;
516     }
517 
518     /**
519      * Creats a string representation of this Database. The representation is given in xml format.
520      * 
521      * @return string representation in xml
522      */
523     @Override
524     public String toString() {
525         StringBuffer result = new StringBuffer();
526 
527         result.append("<?xml version=\"1.0\"?>\n");
528         result.append("<!DOCTYPE database SYSTEM \"" + DTDResolver.WEB_SITE_DTD + "\">\n");
529         result.append("<!-- Autogenerated by SQLToXMLSchema! -->\n");
530         result.append("<database name=\"").append(getName()).append('"').append(" package=\"").append(getPackage())
531                 .append('"').append(" defaultIdMethod=\"").append(getDefaultIdMethod()).append('"')
532                 .append(" baseClass=\"").append(getBaseClass()).append('"').append(" basePeer=\"")
533                 .append(getBasePeer()).append('"').append(">\n");
534 
535         for (Iterator i = tableList.iterator(); i.hasNext();) {
536             result.append(i.next());
537         }
538 
539         result.append("</database>");
540         return result.toString();
541     }
542 
543     /**
544      * Add an XML Specified option key/value pair to this element's option set.
545      * 
546      * @param key
547      *            the key of the option.
548      * @param value
549      *            the value of the option.
550      */
551     public void addOption(String key, String value) {
552         options.put(key, value);
553     }
554 
555     /**
556      * Get the value that was associated with this key in an XML option element.
557      * 
558      * @param key
559      *            the key of the option.
560      * @return The value for the key or a null.
561      */
562     public String getOption(String key) {
563         return (String) options.get(key);
564     }
565 
566     /**
567      * Gets the full ordered hashtable array of items specified by XML option statements under this element.
568      * <p>
569      * 
570      * Note, this is not thread save but since it's only used for generation which is single threaded, there should be
571      * minimum danger using this in Velocity.
572      * 
573      * @return An Map of all options. Will not be null but may be empty.
574      */
575     public Map getOptions() {
576         return options;
577     }
578 }