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