View Javadoc

1   package org.apache.torque.engine.database.model;
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.util.ArrayList;
23  import java.util.Collections;
24  import java.util.HashMap;
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.collections.map.ListOrderedMap;
31  import org.apache.commons.logging.Log;
32  import org.apache.commons.logging.LogFactory;
33  import org.apache.torque.engine.EngineException;
34  import org.apache.torque.engine.database.transform.DTDResolver;
35  import org.apache.torque.engine.platform.Platform;
36  import org.apache.torque.engine.platform.PlatformFactory;
37  import org.xml.sax.Attributes;
38  
39  /**
40   * A class for holding application data structures.
41   * 
42   * @author <a href="mailto:leon@opticode.co.za>Leon Messerschmidt</a>
43   * @author <a href="mailto:jmcnally@collab.net>John McNally</a>
44   * @author <a href="mailto:mpoeschl@marmot.at>Martin Poeschl</a>
45   * @author <a href="mailto:dlr@collab.net>Daniel Rall</a>
46   * @author <a href="mailto:byron_foster@byron_foster@yahoo.com>Byron Foster</a>
47   * @author <a href="mailto:monroe@dukece.com>Greg Monroe</a>
48   * @version $Id: Database.java,v 1.1.6.1 2008-04-01 04:07:48 jkeller Exp $
49   */
50  public class Database {
51  	/** Logging class from commons.logging */
52  	private static Log log = LogFactory.getLog(Database.class);
53  
54  	private String databaseType = null;
55  	private List tableList = new ArrayList(100);
56  	private Map domainMap = new HashMap();
57  	private String name;
58  	private String javaName;
59  	private String pkg;
60  	private String baseClass;
61  	private String basePeer;
62  	private String defaultIdMethod;
63  	private String defaultJavaType;
64  	private String defaultJavaNamingMethod;
65  	private Hashtable tablesByName = new Hashtable();
66  	private Hashtable tablesByJavaName = new Hashtable();
67  	private boolean heavyIndexing;
68  	/** the name of the definition file */
69  	private String fileName;
70  	private Map options = Collections.synchronizedMap(new ListOrderedMap());
71  
72  	/**
73  	 * Creates a new instance for the specified database type.
74  	 * 
75  	 * @param databaseType
76  	 *            The default type for this database.
77  	 */
78  	public Database(String databaseType) {
79  		this.databaseType = databaseType;
80  	}
81  
82  	/**
83  	 * Load the database object from an xml tag.
84  	 * 
85  	 * @param attrib
86  	 *            the xml attributes
87  	 */
88  	public void loadFromXML(Attributes attrib) {
89  		setName(attrib.getValue("name"));
90  		pkg = attrib.getValue("package");
91  		baseClass = attrib.getValue("baseClass");
92  		basePeer = attrib.getValue("basePeer");
93  		defaultJavaType = attrib.getValue("defaultJavaType");
94  		defaultIdMethod = attrib.getValue("defaultIdMethod");
95  		defaultJavaNamingMethod = attrib.getValue("defaultJavaNamingMethod");
96  		if (defaultJavaNamingMethod == null) {
97  			defaultJavaNamingMethod = NameGenerator.CONV_METHOD_UNDERSCORE;
98  		}
99  		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 }