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 473814 2006-11-11 22:30:30Z tv $
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
071: .synchronizedMap(new ListOrderedMap());
072:
073: /**
074: * Creates a new instance for the specified database type.
075: *
076: * @param databaseType 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 the xml attributes
086: */
087: public void loadFromXML(Attributes attrib) {
088: setName(attrib.getValue("name"));
089: pkg = attrib.getValue("package");
090: baseClass = attrib.getValue("baseClass");
091: basePeer = attrib.getValue("basePeer");
092: defaultJavaType = attrib.getValue("defaultJavaType");
093: defaultIdMethod = attrib.getValue("defaultIdMethod");
094: defaultJavaNamingMethod = attrib
095: .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 name of the Database
115: */
116: public void setName(String name) {
117: /** @task check this */
118: // this.name = (name == null ? Torque.getDefaultDB() : name);
119: this .name = (name == null ? "default" : name);
120: }
121:
122: public String getFileName() {
123: return fileName;
124: }
125:
126: public void setFileName(String name) {
127: this .fileName = name;
128: }
129:
130: /**
131: * Get the value of package.
132: * @return value of package.
133: */
134: public String getPackage() {
135: return pkg;
136: }
137:
138: /**
139: * Set the value of package.
140: * @param v Value to assign to package.
141: */
142: public void setPackage(String v) {
143: this .pkg = v;
144: }
145:
146: /**
147: * Get the value of baseClass.
148: * @return value of baseClass.
149: */
150: public String getBaseClass() {
151: if (baseClass == null) {
152: return "BaseObject";
153: }
154: return baseClass;
155: }
156:
157: /**
158: * Set the value of baseClass.
159: * @param v Value to assign to baseClass.
160: */
161: public void setBaseClass(String v) {
162: this .baseClass = v;
163: }
164:
165: /**
166: * Get the value of basePeer.
167: * @return value of basePeer.
168: */
169: public String getBasePeer() {
170: if (basePeer == null) {
171: return "BasePeer";
172: }
173: return basePeer;
174: }
175:
176: /**
177: * Set the value of basePeer.
178: * @param v Value to assign to basePeer.
179: */
180: public void setBasePeer(String v) {
181: this .basePeer = v;
182: }
183:
184: /**
185: * Get the value of defaultIdMethod.
186: * @return value of defaultIdMethod.
187: */
188: public String getDefaultIdMethod() {
189: return defaultIdMethod;
190: }
191:
192: /**
193: * Set the value of defaultIdMethod.
194: * @param v Value to assign to defaultIdMethod.
195: */
196: public void setDefaultIdMethod(String v) {
197: this .defaultIdMethod = v;
198: }
199:
200: /**
201: * Get type to use in Java sources (primitive || object)
202: *
203: * @return the type to use
204: */
205: public String getDefaultJavaType() {
206: return defaultJavaType;
207: }
208:
209: /**
210: * Get the value of defaultJavaNamingMethod which specifies the
211: * method for converting schema names for table and column to Java names.
212: *
213: * @return The default naming conversion used by this database.
214: */
215: public String getDefaultJavaNamingMethod() {
216: return defaultJavaNamingMethod;
217: }
218:
219: /**
220: * Set the value of defaultJavaNamingMethod.
221: * @param v The default naming conversion for this database to use.
222: */
223: public void setDefaultJavaNamingMethod(String v) {
224: this .defaultJavaNamingMethod = v;
225: }
226:
227: /**
228: * Get the value of heavyIndexing.
229: * @return value of heavyIndexing.
230: */
231: public boolean isHeavyIndexing() {
232: return heavyIndexing;
233: }
234:
235: /**
236: * Set the value of heavyIndexing.
237: * @param v Value to assign to heavyIndexing.
238: */
239: public void setHeavyIndexing(boolean v) {
240: this .heavyIndexing = v;
241: }
242:
243: /**
244: * Return an List of all tables
245: *
246: * @return List of all tables
247: */
248: public List getTables() {
249: return tableList;
250: }
251:
252: /**
253: * Return the table with the specified name.
254: *
255: * @param name table name
256: * @return A Table object. If it does not exist it returns null
257: */
258: public Table getTable(String name) {
259: return (Table) tablesByName.get(name);
260: }
261:
262: /**
263: * Return the table with the specified javaName.
264: *
265: * @param javaName name of the java object representing the table
266: * @return A Table object. If it does not exist it returns null
267: */
268: public Table getTableByJavaName(String javaName) {
269: return (Table) tablesByJavaName.get(javaName);
270: }
271:
272: /**
273: * An utility method to add a new table from an xml attribute.
274: *
275: * @param attrib the xml attributes
276: * @return the created Table
277: */
278: public Table addTable(Attributes attrib) {
279: Table tbl = new Table();
280: tbl.setDatabase(this );
281: tbl.loadFromXML(attrib, this .getDefaultIdMethod());
282: addTable(tbl);
283: return tbl;
284: }
285:
286: /**
287: * Add a table to the list and sets the Database property to this Database
288: *
289: * @param tbl the table to add
290: */
291: public void addTable(Table tbl) {
292: tbl.setDatabase(this );
293: tableList.add(tbl);
294: tablesByName.put(tbl.getName(), tbl);
295: tablesByJavaName.put(tbl.getJavaName(), tbl);
296: tbl.setPackage(getPackage());
297: }
298:
299: public void addDomain(Domain domain) {
300: domainMap.put(domain.getName(), domain);
301: }
302:
303: public Domain getDomain(String domainName) {
304: return (Domain) domainMap.get(domainName);
305: }
306:
307: protected String getDatabaseType() {
308: return databaseType;
309: }
310:
311: public void setDatabaseType(String databaseType) {
312: this .databaseType = databaseType;
313: }
314:
315: /**
316: * Returns the Platform implementation for this database.
317: *
318: * @return a Platform implementation
319: */
320: public Platform getPlatform() {
321: return PlatformFactory.getPlatformFor(databaseType);
322: }
323:
324: /**
325: * Determines if this database will be using the
326: * <code>IDMethod.ID_BROKER</code> to create ids for torque OM
327: * objects.
328: * @return true if there is at least one table in this database that
329: * uses the <code>IDMethod.ID_BROKER</code> method of generating
330: * ids. returns false otherwise.
331: */
332: public boolean requiresIdTable() {
333: Iterator iter = getTables().iterator();
334: while (iter.hasNext()) {
335: Table table = (Table) iter.next();
336: if (table.getIdMethod().equals(IDMethod.ID_BROKER)) {
337: return true;
338: }
339: }
340: return false;
341: }
342:
343: /**
344: * Initializes the model.
345: *
346: * @throws EngineException
347: */
348: public void doFinalInitialization() throws EngineException {
349: Iterator iter = getTables().iterator();
350: while (iter.hasNext()) {
351: Table currTable = (Table) iter.next();
352:
353: // check schema integrity
354: // if idMethod="autoincrement", make sure a column is
355: // specified as autoIncrement="true"
356: // FIXME: Handle idMethod="native" via DB adapter.
357: // TODO autoincrement is no longer supported!!!
358: if (currTable.getIdMethod().equals("autoincrement")) {
359: boolean foundOne = false;
360: Iterator colIter = currTable.getColumns().iterator();
361: while (colIter.hasNext() && !foundOne) {
362: foundOne = ((Column) colIter.next())
363: .isAutoIncrement();
364: }
365:
366: if (!foundOne) {
367: String errorMessage = "Table '"
368: + currTable.getName()
369: + "' is marked as autoincrement, but it does not "
370: + "have a column which declared as the one to "
371: + "auto increment (i.e. autoIncrement=\"true\")\n";
372: throw new EngineException("Error in XML schema: "
373: + errorMessage);
374: }
375: }
376:
377: currTable.doFinalInitialization();
378:
379: // setup reverse fk relations
380: Iterator fks = currTable.getForeignKeys().iterator();
381: while (fks.hasNext()) {
382: ForeignKey currFK = (ForeignKey) fks.next();
383: Table foreignTable = getTable(currFK
384: .getForeignTableName());
385: if (foreignTable == null) {
386: throw new EngineException("Attempt to set foreign"
387: + " key to nonexistent table, "
388: + currFK.getForeignTableName());
389: } else {
390: // TODO check type and size
391: List referrers = foreignTable.getReferrers();
392: if ((referrers == null || !referrers
393: .contains(currFK))) {
394: foreignTable.addReferrer(currFK);
395: }
396:
397: // local column references
398: Iterator localColumnNames = currFK
399: .getLocalColumns().iterator();
400: while (localColumnNames.hasNext()) {
401: Column local = currTable
402: .getColumn((String) localColumnNames
403: .next());
404: // give notice of a schema inconsistency.
405: // note we do not prevent the npe as there is nothing
406: // that we can do, if it is to occur.
407: if (local == null) {
408: throw new EngineException(
409: "Attempt to define foreign"
410: + " key with nonexistent column in table, "
411: + currTable.getName());
412: } else {
413: //check for foreign pk's
414: if (local.isPrimaryKey()) {
415: currTable.setContainsForeignPK(true);
416: }
417: }
418: }
419:
420: // foreign column references
421: Iterator foreignColumnNames = currFK
422: .getForeignColumns().iterator();
423: while (foreignColumnNames.hasNext()) {
424: String foreignColumnName = (String) foreignColumnNames
425: .next();
426: Column foreign = foreignTable
427: .getColumn(foreignColumnName);
428: // if the foreign column does not exist, we may have an
429: // external reference or a misspelling
430: if (foreign == null) {
431: throw new EngineException(
432: "Attempt to set foreign"
433: + " key to nonexistent column: table="
434: + currTable.getName()
435: + ", foreign column="
436: + foreignColumnName);
437: } else {
438: foreign.addReferrer(currFK);
439: }
440: }
441: }
442: }
443: }
444: }
445:
446: /**
447: * Get the base name to use when creating related Java Classes.
448: *
449: * @return A Java syntax capatible version of the dbName using the method
450: * defined by the defaultJavaNamingMethod XML value.
451: */
452: public String getJavaName() {
453: if (javaName == null) {
454: List inputs = new ArrayList(2);
455: inputs.add(name);
456: inputs.add(defaultJavaNamingMethod);
457: try {
458: javaName = NameFactory.generateName(
459: NameFactory.JAVA_GENERATOR, inputs);
460: } catch (EngineException e) {
461: log.error(e, e);
462: }
463: }
464: return javaName;
465: }
466:
467: /**
468: * Convert dbName to a Java compatible name by the JavaName method only
469: * (ignores the defaultJavaNamingMethod).
470: *
471: * @return The current dbName converted to a standard format that can
472: * be used as part of a Java Object name.
473: */
474: public String getStandardJavaName() {
475: if (javaName == null) {
476: List inputs = new ArrayList(2);
477: inputs.add(name);
478: inputs.add(NameGenerator.CONV_METHOD_JAVANAME);
479: try {
480: javaName = NameFactory.generateName(
481: NameFactory.JAVA_GENERATOR, inputs);
482: } catch (EngineException e) {
483: log.error(e, e);
484: }
485: }
486: return javaName;
487: }
488:
489: /**
490: * Creats a string representation of this Database.
491: * The representation is given in xml format.
492: *
493: * @return string representation in xml
494: */
495: public String toString() {
496: StringBuffer result = new StringBuffer();
497:
498: result.append("<?xml version=\"1.0\"?>\n");
499: result.append("<!DOCTYPE database SYSTEM \""
500: + DTDResolver.WEB_SITE_DTD + "\">\n");
501: result.append("<!-- Autogenerated by SQLToXMLSchema! -->\n");
502: result.append("<database name=\"").append(getName())
503: .append('"').append(" package=\"").append(getPackage())
504: .append('"').append(" defaultIdMethod=\"").append(
505: getDefaultIdMethod()).append('"').append(
506: " baseClass=\"").append(getBaseClass()).append(
507: '"').append(" basePeer=\"").append(
508: getBasePeer()).append('"').append(">\n");
509:
510: for (Iterator i = tableList.iterator(); i.hasNext();) {
511: result.append(i.next());
512: }
513:
514: result.append("</database>");
515: return result.toString();
516: }
517:
518: /**
519: * Add an XML Specified option key/value pair to this element's option set.
520: *
521: * @param key the key of the option.
522: * @param value the value of the option.
523: */
524: public void addOption(String key, String value) {
525: options.put(key, value);
526: }
527:
528: /**
529: * Get the value that was associated with this key in an XML option
530: * element.
531: *
532: * @param key the key of the option.
533: * @return The value for the key or a null.
534: */
535: public String getOption(String key) {
536: return (String) options.get(key);
537: }
538:
539: /**
540: * Gets the full ordered hashtable array of items specified by XML option
541: * statements under this element.<p>
542: *
543: * Note, this is not thread save but since it's only used for
544: * generation which is single threaded, there should be minimum
545: * danger using this in Velocity.
546: *
547: * @return An Map of all options. Will not be null but may be empty.
548: */
549: public Map getOptions() {
550: return options;
551: }
552: }
|