001 package org.apache.torque.engine.database.model;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements. See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership. The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License. You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied. See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022 import java.util.ArrayList;
023 import java.util.Collections;
024 import java.util.HashMap;
025 import java.util.Hashtable;
026 import java.util.Iterator;
027 import java.util.List;
028 import java.util.Map;
029
030 import org.apache.commons.collections.map.ListOrderedMap;
031 import org.apache.commons.logging.Log;
032 import org.apache.commons.logging.LogFactory;
033 import org.apache.torque.engine.EngineException;
034 import org.apache.torque.engine.database.transform.DTDResolver;
035 import org.apache.torque.engine.platform.Platform;
036 import org.apache.torque.engine.platform.PlatformFactory;
037 import org.xml.sax.Attributes;
038
039 /**
040 * A class for holding application data structures.
041 *
042 * @author <a href="mailto:leon@opticode.co.za>Leon Messerschmidt</a>
043 * @author <a href="mailto:jmcnally@collab.net>John McNally</a>
044 * @author <a href="mailto:mpoeschl@marmot.at>Martin Poeschl</a>
045 * @author <a href="mailto:dlr@collab.net>Daniel Rall</a>
046 * @author <a href="mailto:byron_foster@byron_foster@yahoo.com>Byron Foster</a>
047 * @author <a href="mailto:monroe@dukece.com>Greg Monroe</a>
048 * @version $Id: Database.java,v 1.1.6.1 2008-04-01 04:07:48 jkeller Exp $
049 */
050 public class Database {
051 /** Logging class from commons.logging */
052 private static Log log = LogFactory.getLog(Database.class);
053
054 private String databaseType = null;
055 private List tableList = new ArrayList(100);
056 private Map domainMap = new HashMap();
057 private String name;
058 private String javaName;
059 private String pkg;
060 private String baseClass;
061 private String basePeer;
062 private String defaultIdMethod;
063 private String defaultJavaType;
064 private String defaultJavaNamingMethod;
065 private Hashtable tablesByName = new Hashtable();
066 private Hashtable tablesByJavaName = new Hashtable();
067 private boolean heavyIndexing;
068 /** the name of the definition file */
069 private String fileName;
070 private Map options = Collections.synchronizedMap(new ListOrderedMap());
071
072 /**
073 * Creates a new instance for the specified database type.
074 *
075 * @param databaseType
076 * The default type for this database.
077 */
078 public Database(String databaseType) {
079 this.databaseType = databaseType;
080 }
081
082 /**
083 * Load the database object from an xml tag.
084 *
085 * @param attrib
086 * the xml attributes
087 */
088 public void loadFromXML(Attributes attrib) {
089 setName(attrib.getValue("name"));
090 pkg = attrib.getValue("package");
091 baseClass = attrib.getValue("baseClass");
092 basePeer = attrib.getValue("basePeer");
093 defaultJavaType = attrib.getValue("defaultJavaType");
094 defaultIdMethod = attrib.getValue("defaultIdMethod");
095 defaultJavaNamingMethod = attrib.getValue("defaultJavaNamingMethod");
096 if (defaultJavaNamingMethod == null) {
097 defaultJavaNamingMethod = NameGenerator.CONV_METHOD_UNDERSCORE;
098 }
099 heavyIndexing = "true".equals(attrib.getValue("heavyIndexing"));
100 }
101
102 /**
103 * Get the name of the Database
104 *
105 * @return name of the Database
106 */
107 public String getName() {
108 return name;
109 }
110
111 /**
112 * Set the name of the Database
113 *
114 * @param name
115 * name of the Database
116 */
117 public void setName(String name) {
118 /** @task check this */
119 // this.name = (name == null ? Torque.getDefaultDB() : name);
120 this.name = (name == null ? "default" : name);
121 }
122
123 public String getFileName() {
124 return fileName;
125 }
126
127 public void setFileName(String name) {
128 this.fileName = name;
129 }
130
131 /**
132 * Get the value of package.
133 *
134 * @return value of package.
135 */
136 public String getPackage() {
137 return pkg;
138 }
139
140 /**
141 * Set the value of package.
142 *
143 * @param v
144 * Value to assign to package.
145 */
146 public void setPackage(String v) {
147 this.pkg = v;
148 }
149
150 /**
151 * Get the value of baseClass.
152 *
153 * @return value of baseClass.
154 */
155 public String getBaseClass() {
156 if (baseClass == null) {
157 return "BaseObject";
158 }
159 return baseClass;
160 }
161
162 /**
163 * Set the value of baseClass.
164 *
165 * @param v
166 * Value to assign to baseClass.
167 */
168 public void setBaseClass(String v) {
169 this.baseClass = v;
170 }
171
172 /**
173 * Get the value of basePeer.
174 *
175 * @return value of basePeer.
176 */
177 public String getBasePeer() {
178 if (basePeer == null) {
179 return "BasePeer";
180 }
181 return basePeer;
182 }
183
184 /**
185 * Set the value of basePeer.
186 *
187 * @param v
188 * Value to assign to basePeer.
189 */
190 public void setBasePeer(String v) {
191 this.basePeer = v;
192 }
193
194 /**
195 * Get the value of defaultIdMethod.
196 *
197 * @return value of defaultIdMethod.
198 */
199 public String getDefaultIdMethod() {
200 return defaultIdMethod;
201 }
202
203 /**
204 * Set the value of defaultIdMethod.
205 *
206 * @param v
207 * Value to assign to defaultIdMethod.
208 */
209 public void setDefaultIdMethod(String v) {
210 this.defaultIdMethod = v;
211 }
212
213 /**
214 * Get type to use in Java sources (primitive || object)
215 *
216 * @return the type to use
217 */
218 public String getDefaultJavaType() {
219 return defaultJavaType;
220 }
221
222 /**
223 * Get the value of defaultJavaNamingMethod which specifies the method for converting schema names for table and
224 * column to Java names.
225 *
226 * @return The default naming conversion used by this database.
227 */
228 public String getDefaultJavaNamingMethod() {
229 return defaultJavaNamingMethod;
230 }
231
232 /**
233 * Set the value of defaultJavaNamingMethod.
234 *
235 * @param v
236 * The default naming conversion for this database to use.
237 */
238 public void setDefaultJavaNamingMethod(String v) {
239 this.defaultJavaNamingMethod = v;
240 }
241
242 /**
243 * Get the value of heavyIndexing.
244 *
245 * @return value of heavyIndexing.
246 */
247 public boolean isHeavyIndexing() {
248 return heavyIndexing;
249 }
250
251 /**
252 * Set the value of heavyIndexing.
253 *
254 * @param v
255 * Value to assign to heavyIndexing.
256 */
257 public void setHeavyIndexing(boolean v) {
258 this.heavyIndexing = v;
259 }
260
261 /**
262 * Return an List of all tables
263 *
264 * @return List of all tables
265 */
266 public List getTables() {
267 return tableList;
268 }
269
270 /**
271 * Return the table with the specified name.
272 *
273 * @param name
274 * table name
275 * @return A Table object. If it does not exist it returns null
276 */
277 public Table getTable(String name) {
278 return (Table) tablesByName.get(name);
279 }
280
281 /**
282 * Return the table with the specified javaName.
283 *
284 * @param javaName
285 * name of the java object representing the table
286 * @return A Table object. If it does not exist it returns null
287 */
288 public Table getTableByJavaName(String javaName) {
289 return (Table) tablesByJavaName.get(javaName);
290 }
291
292 /**
293 * An utility method to add a new table from an xml attribute.
294 *
295 * @param attrib
296 * the xml attributes
297 * @return the created Table
298 */
299 public Table addTable(Attributes attrib) {
300 Table tbl = new Table();
301 tbl.setDatabase(this);
302 tbl.loadFromXML(attrib, this.getDefaultIdMethod());
303 addTable(tbl);
304 return tbl;
305 }
306
307 /**
308 * Add a table to the list and sets the Database property to this Database
309 *
310 * @param tbl
311 * the table to add
312 */
313 public void addTable(Table tbl) {
314 tbl.setDatabase(this);
315 tableList.add(tbl);
316 tablesByName.put(tbl.getName(), tbl);
317 tablesByName.put(tbl.getName().toLowerCase(), tbl);
318 tablesByName.put(tbl.getName().toUpperCase(), tbl);
319 tablesByJavaName.put(tbl.getJavaName(), tbl);
320 tbl.setPackage(getPackage());
321 }
322
323 public void addDomain(Domain domain) {
324 domainMap.put(domain.getName(), domain);
325 }
326
327 public Domain getDomain(String domainName) {
328 return (Domain) domainMap.get(domainName);
329 }
330
331 protected String getDatabaseType() {
332 return databaseType;
333 }
334
335 public void setDatabaseType(String databaseType) {
336 this.databaseType = databaseType;
337 }
338
339 /**
340 * Returns the Platform implementation for this database.
341 *
342 * @return a Platform implementation
343 */
344 public Platform getPlatform() {
345 return PlatformFactory.getPlatformFor(databaseType);
346 }
347
348 /**
349 * Determines if this database will be using the <code>IDMethod.ID_BROKER</code> to create ids for torque OM
350 * objects.
351 *
352 * @return true if there is at least one table in this database that uses the <code>IDMethod.ID_BROKER</code> method
353 * of generating ids. returns false otherwise.
354 */
355 public boolean requiresIdTable() {
356 Iterator iter = getTables().iterator();
357 while (iter.hasNext()) {
358 Table table = (Table) iter.next();
359 if (table.getIdMethod().equals(IDMethod.ID_BROKER)) {
360 return true;
361 }
362 }
363 return false;
364 }
365
366 /**
367 * Initializes the model.
368 *
369 * @throws EngineException
370 */
371 public void doFinalInitialization() throws EngineException {
372 Iterator iter = getTables().iterator();
373 while (iter.hasNext()) {
374 Table currTable = (Table) iter.next();
375
376 // check schema integrity
377 // if idMethod="autoincrement", make sure a column is
378 // specified as autoIncrement="true"
379 // FIXME: Handle idMethod="native" via DB adapter.
380 // TODO autoincrement is no longer supported!!!
381 if (currTable.getIdMethod().equals("autoincrement")) {
382 boolean foundOne = false;
383 Iterator colIter = currTable.getColumns().iterator();
384 while (colIter.hasNext() && !foundOne) {
385 foundOne = ((Column) colIter.next()).isAutoIncrement();
386 }
387
388 if (!foundOne) {
389 String errorMessage = "Table '" + currTable.getName() + "' is marked as autoincrement, but it does not " + "have a column which declared as the one to " + "auto increment (i.e. autoIncrement=\"true\")\n";
390 throw new EngineException("Error in XML schema: " + errorMessage);
391 }
392 }
393
394 currTable.doFinalInitialization();
395
396 // setup reverse fk relations
397 Iterator fks = currTable.getForeignKeys().iterator();
398 while (fks.hasNext()) {
399 ForeignKey currFK = (ForeignKey) fks.next();
400 Table foreignTable = getTable(currFK.getForeignTableName());
401 if (foreignTable == null) {
402 throw new EngineException("Attempt to set foreign" + " key to nonexistent table, " + currFK.getForeignTableName());
403 } else {
404 // TODO check type and size
405 List referrers = foreignTable.getReferrers();
406 if ((referrers == null || !referrers.contains(currFK))) {
407 foreignTable.addReferrer(currFK);
408 }
409
410 // local column references
411 Iterator localColumnNames = currFK.getLocalColumns().iterator();
412 while (localColumnNames.hasNext()) {
413 Column local = currTable.getColumn((String) localColumnNames.next());
414 // give notice of a schema inconsistency.
415 // note we do not prevent the npe as there is nothing
416 // that we can do, if it is to occur.
417 if (local == null) {
418 throw new EngineException("Attempt to define foreign" + " key with nonexistent column in table, " + currTable.getName());
419 } else {
420 // check for foreign pk's
421 if (local.isPrimaryKey()) {
422 currTable.setContainsForeignPK(true);
423 }
424 }
425 }
426
427 // foreign column references
428 Iterator foreignColumnNames = currFK.getForeignColumns().iterator();
429 while (foreignColumnNames.hasNext()) {
430 String foreignColumnName = (String) foreignColumnNames.next();
431 Column foreign = foreignTable.getColumn(foreignColumnName);
432 // if the foreign column does not exist, we may have an
433 // external reference or a misspelling
434 if (foreign == null) {
435 throw new EngineException("Attempt to set foreign" + " key to nonexistent column: table=" + currTable.getName() + ", foreign column=" + foreignColumnName);
436 } else {
437 foreign.addReferrer(currFK);
438 }
439 }
440 }
441 }
442 }
443 }
444
445 /**
446 * Get the base name to use when creating related Java Classes.
447 *
448 * @return A Java syntax capatible version of the dbName using the method defined by the defaultJavaNamingMethod XML
449 * value.
450 */
451 public String getJavaName() {
452 if (javaName == null) {
453 List inputs = new ArrayList(2);
454 inputs.add(name);
455 inputs.add(defaultJavaNamingMethod);
456 try {
457 javaName = NameFactory.generateName(NameFactory.JAVA_GENERATOR, inputs);
458 } catch (EngineException e) {
459 log.error(e, e);
460 }
461 }
462 return javaName;
463 }
464
465 /**
466 * Convert dbName to a Java compatible name by the JavaName method only (ignores the defaultJavaNamingMethod).
467 *
468 * @return The current dbName converted to a standard format that can be used as part of a Java Object name.
469 */
470 public String getStandardJavaName() {
471 if (javaName == null) {
472 List inputs = new ArrayList(2);
473 inputs.add(name);
474 inputs.add(NameGenerator.CONV_METHOD_JAVANAME);
475 try {
476 javaName = NameFactory.generateName(NameFactory.JAVA_GENERATOR, inputs);
477 } catch (EngineException e) {
478 log.error(e, e);
479 }
480 }
481 return javaName;
482 }
483
484 /**
485 * Creats a string representation of this Database. The representation is given in xml format.
486 *
487 * @return string representation in xml
488 */
489 public String toString() {
490 StringBuffer result = new StringBuffer();
491
492 result.append("<?xml version=\"1.0\"?>\n");
493 result.append("<!DOCTYPE database SYSTEM \"" + DTDResolver.WEB_SITE_DTD + "\">\n");
494 result.append("<!-- Autogenerated by SQLToXMLSchema! -->\n");
495 result.append("<database name=\"").append(getName()).append('"').append(" package=\"").append(getPackage()).append('"').append(" defaultIdMethod=\"").append(getDefaultIdMethod()).append('"').append(" baseClass=\"").append(getBaseClass()).append('"').append(" basePeer=\"").append(getBasePeer()).append('"').append(">\n");
496
497 for (Iterator i = tableList.iterator(); i.hasNext();) {
498 result.append(i.next());
499 }
500
501 result.append("</database>");
502 return result.toString();
503 }
504
505 /**
506 * Add an XML Specified option key/value pair to this element's option set.
507 *
508 * @param key
509 * the key of the option.
510 * @param value
511 * the value of the option.
512 */
513 public void addOption(String key, String value) {
514 options.put(key, value);
515 }
516
517 /**
518 * Get the value that was associated with this key in an XML option element.
519 *
520 * @param key
521 * the key of the option.
522 * @return The value for the key or a null.
523 */
524 public String getOption(String key) {
525 return (String) options.get(key);
526 }
527
528 /**
529 * Gets the full ordered hashtable array of items specified by XML option statements under this element.
530 * <p>
531 *
532 * Note, this is not thread save but since it's only used for generation which is single threaded, there should be
533 * minimum danger using this in Velocity.
534 *
535 * @return An Map of all options. Will not be null but may be empty.
536 */
537 public Map getOptions() {
538 return options;
539 }
540 }