Classes in this File | Line Coverage | Branch Coverage | Complexity | ||||
Database |
|
| 1.864864864864865;1.865 |
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 | 0 | private static Log log = LogFactory.getLog(Database.class); |
53 | ||
54 | 0 | private String databaseType = null; |
55 | 0 | private List tableList = new ArrayList(100); |
56 | 0 | 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 | 0 | private Hashtable tablesByName = new Hashtable(); |
66 | 0 | private Hashtable tablesByJavaName = new Hashtable(); |
67 | private boolean heavyIndexing; | |
68 | /** the name of the definition file */ | |
69 | private String fileName; | |
70 | 0 | 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 | 0 | public Database(String databaseType) { |
79 | 0 | this.databaseType = databaseType; |
80 | 0 | } |
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 | 0 | setName(attrib.getValue("name")); |
90 | 0 | pkg = attrib.getValue("package"); |
91 | 0 | baseClass = attrib.getValue("baseClass"); |
92 | 0 | basePeer = attrib.getValue("basePeer"); |
93 | 0 | defaultJavaType = attrib.getValue("defaultJavaType"); |
94 | 0 | defaultIdMethod = attrib.getValue("defaultIdMethod"); |
95 | 0 | defaultJavaNamingMethod = attrib.getValue("defaultJavaNamingMethod"); |
96 | 0 | if (defaultJavaNamingMethod == null) { |
97 | 0 | defaultJavaNamingMethod = NameGenerator.CONV_METHOD_UNDERSCORE; |
98 | } | |
99 | 0 | heavyIndexing = "true".equals(attrib.getValue("heavyIndexing")); |
100 | 0 | } |
101 | ||
102 | /** | |
103 | * Get the name of the Database | |
104 | * | |
105 | * @return name of the Database | |
106 | */ | |
107 | public String getName() { | |
108 | 0 | 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 | 0 | this.name = (name == null ? "default" : name); |
121 | 0 | } |
122 | ||
123 | public String getFileName() { | |
124 | 0 | return fileName; |
125 | } | |
126 | ||
127 | public void setFileName(String name) { | |
128 | 0 | this.fileName = name; |
129 | 0 | } |
130 | ||
131 | /** | |
132 | * Get the value of package. | |
133 | * | |
134 | * @return value of package. | |
135 | */ | |
136 | public String getPackage() { | |
137 | 0 | 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 | 0 | this.pkg = v; |
148 | 0 | } |
149 | ||
150 | /** | |
151 | * Get the value of baseClass. | |
152 | * | |
153 | * @return value of baseClass. | |
154 | */ | |
155 | public String getBaseClass() { | |
156 | 0 | if (baseClass == null) { |
157 | 0 | return "BaseObject"; |
158 | } | |
159 | 0 | 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 | 0 | this.baseClass = v; |
170 | 0 | } |
171 | ||
172 | /** | |
173 | * Get the value of basePeer. | |
174 | * | |
175 | * @return value of basePeer. | |
176 | */ | |
177 | public String getBasePeer() { | |
178 | 0 | if (basePeer == null) { |
179 | 0 | return "BasePeer"; |
180 | } | |
181 | 0 | 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 | 0 | this.basePeer = v; |
192 | 0 | } |
193 | ||
194 | /** | |
195 | * Get the value of defaultIdMethod. | |
196 | * | |
197 | * @return value of defaultIdMethod. | |
198 | */ | |
199 | public String getDefaultIdMethod() { | |
200 | 0 | 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 | 0 | this.defaultIdMethod = v; |
211 | 0 | } |
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 | 0 | 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 | 0 | 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 | 0 | this.defaultJavaNamingMethod = v; |
240 | 0 | } |
241 | ||
242 | /** | |
243 | * Get the value of heavyIndexing. | |
244 | * | |
245 | * @return value of heavyIndexing. | |
246 | */ | |
247 | public boolean isHeavyIndexing() { | |
248 | 0 | 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 | 0 | this.heavyIndexing = v; |
259 | 0 | } |
260 | ||
261 | /** | |
262 | * Return an List of all tables | |
263 | * | |
264 | * @return List of all tables | |
265 | */ | |
266 | public List getTables() { | |
267 | 0 | 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 | 0 | 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 | 0 | 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 | 0 | Table tbl = new Table(); |
301 | 0 | tbl.setDatabase(this); |
302 | 0 | tbl.loadFromXML(attrib, this.getDefaultIdMethod()); |
303 | 0 | addTable(tbl); |
304 | 0 | 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 | 0 | tbl.setDatabase(this); |
315 | 0 | tableList.add(tbl); |
316 | 0 | tablesByName.put(tbl.getName(), tbl); |
317 | 0 | tablesByName.put(tbl.getName().toLowerCase(), tbl); |
318 | 0 | tablesByName.put(tbl.getName().toUpperCase(), tbl); |
319 | 0 | tablesByJavaName.put(tbl.getJavaName(), tbl); |
320 | 0 | tbl.setPackage(getPackage()); |
321 | 0 | } |
322 | ||
323 | public void addDomain(Domain domain) { | |
324 | 0 | domainMap.put(domain.getName(), domain); |
325 | 0 | } |
326 | ||
327 | public Domain getDomain(String domainName) { | |
328 | 0 | return (Domain) domainMap.get(domainName); |
329 | } | |
330 | ||
331 | protected String getDatabaseType() { | |
332 | 0 | return databaseType; |
333 | } | |
334 | ||
335 | public void setDatabaseType(String databaseType) { | |
336 | 0 | this.databaseType = databaseType; |
337 | 0 | } |
338 | ||
339 | /** | |
340 | * Returns the Platform implementation for this database. | |
341 | * | |
342 | * @return a Platform implementation | |
343 | */ | |
344 | public Platform getPlatform() { | |
345 | 0 | 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 | 0 | Iterator iter = getTables().iterator(); |
357 | 0 | while (iter.hasNext()) { |
358 | 0 | Table table = (Table) iter.next(); |
359 | 0 | if (table.getIdMethod().equals(IDMethod.ID_BROKER)) { |
360 | 0 | return true; |
361 | } | |
362 | 0 | } |
363 | 0 | return false; |
364 | } | |
365 | ||
366 | /** | |
367 | * Initializes the model. | |
368 | * | |
369 | * @throws EngineException | |
370 | */ | |
371 | public void doFinalInitialization() throws EngineException { | |
372 | 0 | Iterator iter = getTables().iterator(); |
373 | 0 | while (iter.hasNext()) { |
374 | 0 | 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 | 0 | if (currTable.getIdMethod().equals("autoincrement")) { |
382 | 0 | boolean foundOne = false; |
383 | 0 | Iterator colIter = currTable.getColumns().iterator(); |
384 | 0 | while (colIter.hasNext() && !foundOne) { |
385 | 0 | foundOne = ((Column) colIter.next()).isAutoIncrement(); |
386 | } | |
387 | ||
388 | 0 | if (!foundOne) { |
389 | 0 | 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 | 0 | throw new EngineException("Error in XML schema: " + errorMessage); |
391 | } | |
392 | } | |
393 | ||
394 | 0 | currTable.doFinalInitialization(); |
395 | ||
396 | // setup reverse fk relations | |
397 | 0 | Iterator fks = currTable.getForeignKeys().iterator(); |
398 | 0 | while (fks.hasNext()) { |
399 | 0 | ForeignKey currFK = (ForeignKey) fks.next(); |
400 | 0 | Table foreignTable = getTable(currFK.getForeignTableName()); |
401 | 0 | if (foreignTable == null) { |
402 | 0 | throw new EngineException("Attempt to set foreign" + " key to nonexistent table, " + currFK.getForeignTableName()); |
403 | } else { | |
404 | // TODO check type and size | |
405 | 0 | List referrers = foreignTable.getReferrers(); |
406 | 0 | if ((referrers == null || !referrers.contains(currFK))) { |
407 | 0 | foreignTable.addReferrer(currFK); |
408 | } | |
409 | ||
410 | // local column references | |
411 | 0 | Iterator localColumnNames = currFK.getLocalColumns().iterator(); |
412 | 0 | while (localColumnNames.hasNext()) { |
413 | 0 | 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 | 0 | if (local == null) { |
418 | 0 | throw new EngineException("Attempt to define foreign" + " key with nonexistent column in table, " + currTable.getName()); |
419 | } else { | |
420 | // check for foreign pk's | |
421 | 0 | if (local.isPrimaryKey()) { |
422 | 0 | currTable.setContainsForeignPK(true); |
423 | } | |
424 | } | |
425 | 0 | } |
426 | ||
427 | // foreign column references | |
428 | 0 | Iterator foreignColumnNames = currFK.getForeignColumns().iterator(); |
429 | 0 | while (foreignColumnNames.hasNext()) { |
430 | 0 | String foreignColumnName = (String) foreignColumnNames.next(); |
431 | 0 | Column foreign = foreignTable.getColumn(foreignColumnName); |
432 | // if the foreign column does not exist, we may have an | |
433 | // external reference or a misspelling | |
434 | 0 | if (foreign == null) { |
435 | 0 | throw new EngineException("Attempt to set foreign" + " key to nonexistent column: table=" + currTable.getName() + ", foreign column=" + foreignColumnName); |
436 | } else { | |
437 | 0 | foreign.addReferrer(currFK); |
438 | } | |
439 | 0 | } |
440 | } | |
441 | 0 | } |
442 | 0 | } |
443 | 0 | } |
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 | 0 | if (javaName == null) { |
453 | 0 | List inputs = new ArrayList(2); |
454 | 0 | inputs.add(name); |
455 | 0 | inputs.add(defaultJavaNamingMethod); |
456 | try { | |
457 | 0 | javaName = NameFactory.generateName(NameFactory.JAVA_GENERATOR, inputs); |
458 | 0 | } catch (EngineException e) { |
459 | 0 | log.error(e, e); |
460 | 0 | } |
461 | } | |
462 | 0 | 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 | 0 | if (javaName == null) { |
472 | 0 | List inputs = new ArrayList(2); |
473 | 0 | inputs.add(name); |
474 | 0 | inputs.add(NameGenerator.CONV_METHOD_JAVANAME); |
475 | try { | |
476 | 0 | javaName = NameFactory.generateName(NameFactory.JAVA_GENERATOR, inputs); |
477 | 0 | } catch (EngineException e) { |
478 | 0 | log.error(e, e); |
479 | 0 | } |
480 | } | |
481 | 0 | 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 | 0 | StringBuffer result = new StringBuffer(); |
491 | ||
492 | 0 | result.append("<?xml version=\"1.0\"?>\n"); |
493 | 0 | result.append("<!DOCTYPE database SYSTEM \"" + DTDResolver.WEB_SITE_DTD + "\">\n"); |
494 | 0 | result.append("<!-- Autogenerated by SQLToXMLSchema! -->\n"); |
495 | 0 | 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 | 0 | for (Iterator i = tableList.iterator(); i.hasNext();) { |
498 | 0 | result.append(i.next()); |
499 | } | |
500 | ||
501 | 0 | result.append("</database>"); |
502 | 0 | 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 | 0 | options.put(key, value); |
515 | 0 | } |
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 | 0 | 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 | 0 | return options; |
539 | } | |
540 | } |