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 or more contributor license agreements. See the NOTICE | |
5 | * file distributed with this work for additional information regarding copyright ownership. The ASF licenses this file | |
6 | * to you under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the | |
7 | * License. You may obtain a copy of the License at | |
8 | * | |
9 | * http://www.apache.org/licenses/LICENSE-2.0 | |
10 | * | |
11 | * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on | |
12 | * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the | |
13 | * specific language governing permissions and limitations under the License. | |
14 | */ | |
15 | ||
16 | import java.util.ArrayList; | |
17 | import java.util.Collections; | |
18 | import java.util.HashMap; | |
19 | import java.util.Hashtable; | |
20 | import java.util.Iterator; | |
21 | import java.util.List; | |
22 | import java.util.Map; | |
23 | ||
24 | import org.apache.commons.collections.map.ListOrderedMap; | |
25 | import org.apache.commons.logging.Log; | |
26 | import org.apache.commons.logging.LogFactory; | |
27 | import org.apache.torque.engine.EngineException; | |
28 | import org.apache.torque.engine.database.transform.DTDResolver; | |
29 | import org.apache.torque.engine.platform.Platform; | |
30 | import org.apache.torque.engine.platform.PlatformFactory; | |
31 | import org.xml.sax.Attributes; | |
32 | ||
33 | /** | |
34 | * A class for holding application data structures. | |
35 | * | |
36 | * @author <a href="mailto:leon@opticode.co.za>Leon Messerschmidt</a> | |
37 | * @author <a href="mailto:jmcnally@collab.net>John McNally</a> | |
38 | * @author <a href="mailto:mpoeschl@marmot.at>Martin Poeschl</a> | |
39 | * @author <a href="mailto:dlr@collab.net>Daniel Rall</a> | |
40 | * @author <a href="mailto:byron_foster@byron_foster@yahoo.com>Byron Foster</a> | |
41 | * @author <a href="mailto:monroe@dukece.com>Greg Monroe</a> | |
42 | * @version $Id: Database.java,v 1.1.6.1 2008-04-01 04:07:48 jkeller Exp $ | |
43 | */ | |
44 | public class Database { | |
45 | /** Logging class from commons.logging */ | |
46 | 0 | private static Log log = LogFactory.getLog(Database.class); |
47 | ||
48 | 0 | private String databaseType = null; |
49 | 0 | private List<Table> tableList = new ArrayList<Table>(100); |
50 | 0 | private Map domainMap = new HashMap(); |
51 | private String name; | |
52 | private String javaName; | |
53 | private String pkg; | |
54 | private String baseClass; | |
55 | private String basePeer; | |
56 | private String defaultIdMethod; | |
57 | private String defaultJavaType; | |
58 | private String defaultJavaNamingMethod; | |
59 | 0 | private Hashtable tablesByName = new Hashtable(); |
60 | 0 | private Hashtable tablesByJavaName = new Hashtable(); |
61 | private boolean heavyIndexing; | |
62 | /** the name of the definition file */ | |
63 | private String fileName; | |
64 | 0 | private Map options = Collections.synchronizedMap(new ListOrderedMap()); |
65 | ||
66 | /** | |
67 | * Creates a new instance for the specified database type. | |
68 | * | |
69 | * @param databaseType | |
70 | * The default type for this database. | |
71 | */ | |
72 | 0 | public Database(String databaseType) { |
73 | 0 | this.databaseType = databaseType; |
74 | 0 | } |
75 | ||
76 | /** | |
77 | * Load the database object from an xml tag. | |
78 | * | |
79 | * @param attrib | |
80 | * the xml attributes | |
81 | */ | |
82 | public void loadFromXML(Attributes attrib) { | |
83 | 0 | setName(attrib.getValue("name")); |
84 | 0 | pkg = attrib.getValue("package"); |
85 | 0 | baseClass = attrib.getValue("baseClass"); |
86 | 0 | basePeer = attrib.getValue("basePeer"); |
87 | 0 | defaultJavaType = attrib.getValue("defaultJavaType"); |
88 | 0 | defaultIdMethod = attrib.getValue("defaultIdMethod"); |
89 | 0 | defaultJavaNamingMethod = attrib.getValue("defaultJavaNamingMethod"); |
90 | 0 | if (defaultJavaNamingMethod == null) { |
91 | 0 | defaultJavaNamingMethod = NameGenerator.CONV_METHOD_UNDERSCORE; |
92 | } | |
93 | 0 | heavyIndexing = "true".equals(attrib.getValue("heavyIndexing")); |
94 | 0 | } |
95 | ||
96 | /** | |
97 | * Get the name of the Database | |
98 | * | |
99 | * @return name of the Database | |
100 | */ | |
101 | public String getName() { | |
102 | 0 | return name; |
103 | } | |
104 | ||
105 | /** | |
106 | * Set the name of the Database | |
107 | * | |
108 | * @param name | |
109 | * name of the Database | |
110 | */ | |
111 | public void setName(String name) { | |
112 | /** @task check this */ | |
113 | // this.name = (name == null ? Torque.getDefaultDB() : name); | |
114 | 0 | this.name = (name == null ? "default" : name); |
115 | 0 | } |
116 | ||
117 | public String getFileName() { | |
118 | 0 | return fileName; |
119 | } | |
120 | ||
121 | public void setFileName(String name) { | |
122 | 0 | this.fileName = name; |
123 | 0 | } |
124 | ||
125 | /** | |
126 | * Get the value of package. | |
127 | * | |
128 | * @return value of package. | |
129 | */ | |
130 | public String getPackage() { | |
131 | 0 | return pkg; |
132 | } | |
133 | ||
134 | /** | |
135 | * Set the value of package. | |
136 | * | |
137 | * @param v | |
138 | * Value to assign to package. | |
139 | */ | |
140 | public void setPackage(String v) { | |
141 | 0 | this.pkg = v; |
142 | 0 | } |
143 | ||
144 | /** | |
145 | * Get the value of baseClass. | |
146 | * | |
147 | * @return value of baseClass. | |
148 | */ | |
149 | public String getBaseClass() { | |
150 | 0 | if (baseClass == null) { |
151 | 0 | return "BaseObject"; |
152 | } | |
153 | 0 | return baseClass; |
154 | } | |
155 | ||
156 | /** | |
157 | * Set the value of baseClass. | |
158 | * | |
159 | * @param v | |
160 | * Value to assign to baseClass. | |
161 | */ | |
162 | public void setBaseClass(String v) { | |
163 | 0 | this.baseClass = v; |
164 | 0 | } |
165 | ||
166 | /** | |
167 | * Get the value of basePeer. | |
168 | * | |
169 | * @return value of basePeer. | |
170 | */ | |
171 | public String getBasePeer() { | |
172 | 0 | if (basePeer == null) { |
173 | 0 | return "BasePeer"; |
174 | } | |
175 | 0 | return basePeer; |
176 | } | |
177 | ||
178 | /** | |
179 | * Set the value of basePeer. | |
180 | * | |
181 | * @param v | |
182 | * Value to assign to basePeer. | |
183 | */ | |
184 | public void setBasePeer(String v) { | |
185 | 0 | this.basePeer = v; |
186 | 0 | } |
187 | ||
188 | /** | |
189 | * Get the value of defaultIdMethod. | |
190 | * | |
191 | * @return value of defaultIdMethod. | |
192 | */ | |
193 | public String getDefaultIdMethod() { | |
194 | 0 | return defaultIdMethod; |
195 | } | |
196 | ||
197 | /** | |
198 | * Set the value of defaultIdMethod. | |
199 | * | |
200 | * @param v | |
201 | * Value to assign to defaultIdMethod. | |
202 | */ | |
203 | public void setDefaultIdMethod(String v) { | |
204 | 0 | this.defaultIdMethod = v; |
205 | 0 | } |
206 | ||
207 | /** | |
208 | * Get type to use in Java sources (primitive || object) | |
209 | * | |
210 | * @return the type to use | |
211 | */ | |
212 | public String getDefaultJavaType() { | |
213 | 0 | return defaultJavaType; |
214 | } | |
215 | ||
216 | /** | |
217 | * Get the value of defaultJavaNamingMethod which specifies the method for converting schema names for table and | |
218 | * column to Java names. | |
219 | * | |
220 | * @return The default naming conversion used by this database. | |
221 | */ | |
222 | public String getDefaultJavaNamingMethod() { | |
223 | 0 | return defaultJavaNamingMethod; |
224 | } | |
225 | ||
226 | /** | |
227 | * Set the value of defaultJavaNamingMethod. | |
228 | * | |
229 | * @param v | |
230 | * The default naming conversion for this database to use. | |
231 | */ | |
232 | public void setDefaultJavaNamingMethod(String v) { | |
233 | 0 | this.defaultJavaNamingMethod = v; |
234 | 0 | } |
235 | ||
236 | /** | |
237 | * Get the value of heavyIndexing. | |
238 | * | |
239 | * @return value of heavyIndexing. | |
240 | */ | |
241 | public boolean isHeavyIndexing() { | |
242 | 0 | return heavyIndexing; |
243 | } | |
244 | ||
245 | /** | |
246 | * Set the value of heavyIndexing. | |
247 | * | |
248 | * @param v | |
249 | * Value to assign to heavyIndexing. | |
250 | */ | |
251 | public void setHeavyIndexing(boolean v) { | |
252 | 0 | this.heavyIndexing = v; |
253 | 0 | } |
254 | ||
255 | /** | |
256 | * Return an List of all tables | |
257 | * | |
258 | * @return List of all tables | |
259 | */ | |
260 | public List<Table> getTables() { | |
261 | 0 | return tableList; |
262 | } | |
263 | ||
264 | /** | |
265 | * Return the table with the specified name. | |
266 | * | |
267 | * @param name | |
268 | * table name | |
269 | * @return A Table object. If it does not exist it returns null | |
270 | */ | |
271 | public Table getTable(String name) { | |
272 | 0 | return (Table) tablesByName.get(name); |
273 | } | |
274 | ||
275 | /** | |
276 | * Return the table with the specified javaName. | |
277 | * | |
278 | * @param javaName | |
279 | * name of the java object representing the table | |
280 | * @return A Table object. If it does not exist it returns null | |
281 | */ | |
282 | public Table getTableByJavaName(String javaName) { | |
283 | 0 | return (Table) tablesByJavaName.get(javaName); |
284 | } | |
285 | ||
286 | /** | |
287 | * An utility method to add a new table from an xml attribute. | |
288 | * | |
289 | * @param attrib | |
290 | * the xml attributes | |
291 | * @return the created Table | |
292 | */ | |
293 | public Table addTable(Attributes attrib) { | |
294 | 0 | Table tbl = new Table(); |
295 | 0 | tbl.setDatabase(this); |
296 | 0 | tbl.loadFromXML(attrib, this.getDefaultIdMethod()); |
297 | 0 | addTable(tbl); |
298 | 0 | return tbl; |
299 | } | |
300 | ||
301 | /** | |
302 | * Add a table to the list and sets the Database property to this Database | |
303 | * | |
304 | * @param tbl | |
305 | * the table to add | |
306 | */ | |
307 | public void addTable(Table tbl) { | |
308 | 0 | tbl.setDatabase(this); |
309 | 0 | tableList.add(tbl); |
310 | 0 | tablesByName.put(tbl.getName(), tbl); |
311 | 0 | tablesByName.put(tbl.getName().toLowerCase(), tbl); |
312 | 0 | tablesByName.put(tbl.getName().toUpperCase(), tbl); |
313 | 0 | tablesByJavaName.put(tbl.getJavaName(), tbl); |
314 | 0 | tbl.setPackage(getPackage()); |
315 | 0 | } |
316 | ||
317 | public void addDomain(Domain domain) { | |
318 | 0 | domainMap.put(domain.getName(), domain); |
319 | 0 | } |
320 | ||
321 | public Domain getDomain(String domainName) { | |
322 | 0 | return (Domain) domainMap.get(domainName); |
323 | } | |
324 | ||
325 | protected String getDatabaseType() { | |
326 | 0 | return databaseType; |
327 | } | |
328 | ||
329 | public void setDatabaseType(String databaseType) { | |
330 | 0 | this.databaseType = databaseType; |
331 | 0 | } |
332 | ||
333 | /** | |
334 | * Returns the Platform implementation for this database. | |
335 | * | |
336 | * @return a Platform implementation | |
337 | */ | |
338 | public Platform getPlatform() { | |
339 | 0 | return PlatformFactory.getPlatformFor(databaseType); |
340 | } | |
341 | ||
342 | /** | |
343 | * Determines if this database will be using the <code>IDMethod.ID_BROKER</code> to create ids for torque OM | |
344 | * objects. | |
345 | * | |
346 | * @return true if there is at least one table in this database that uses the <code>IDMethod.ID_BROKER</code> method | |
347 | * of generating ids. returns false otherwise. | |
348 | */ | |
349 | public boolean requiresIdTable() { | |
350 | 0 | Iterator iter = getTables().iterator(); |
351 | 0 | while (iter.hasNext()) { |
352 | 0 | Table table = (Table) iter.next(); |
353 | 0 | if (table.getIdMethod().equals(IDMethod.ID_BROKER)) { |
354 | 0 | return true; |
355 | } | |
356 | 0 | } |
357 | 0 | return false; |
358 | } | |
359 | ||
360 | /** | |
361 | * Initializes the model. | |
362 | * | |
363 | * @throws EngineException | |
364 | */ | |
365 | public void doFinalInitialization() throws EngineException { | |
366 | 0 | Iterator iter = getTables().iterator(); |
367 | 0 | while (iter.hasNext()) { |
368 | 0 | Table currTable = (Table) iter.next(); |
369 | ||
370 | // check schema integrity | |
371 | // if idMethod="autoincrement", make sure a column is | |
372 | // specified as autoIncrement="true" | |
373 | // FIXME: Handle idMethod="native" via DB adapter. | |
374 | // TODO autoincrement is no longer supported!!! | |
375 | 0 | if (currTable.getIdMethod().equals("autoincrement")) { |
376 | 0 | boolean foundOne = false; |
377 | 0 | Iterator colIter = currTable.getColumns().iterator(); |
378 | 0 | while (colIter.hasNext() && !foundOne) { |
379 | 0 | foundOne = ((Column) colIter.next()).isAutoIncrement(); |
380 | } | |
381 | ||
382 | 0 | if (!foundOne) { |
383 | 0 | String errorMessage = "Table '" + currTable.getName() |
384 | + "' is marked as autoincrement, but it does not " | |
385 | + "have a column which declared as the one to " | |
386 | + "auto increment (i.e. autoIncrement=\"true\")\n"; | |
387 | 0 | throw new EngineException("Error in XML schema: " + errorMessage); |
388 | } | |
389 | } | |
390 | ||
391 | 0 | currTable.doFinalInitialization(); |
392 | ||
393 | // setup reverse fk relations | |
394 | 0 | Iterator fks = currTable.getForeignKeys().iterator(); |
395 | 0 | while (fks.hasNext()) { |
396 | 0 | ForeignKey currFK = (ForeignKey) fks.next(); |
397 | 0 | Table foreignTable = getTable(currFK.getForeignTableName()); |
398 | 0 | if (foreignTable == null) { |
399 | 0 | throw new EngineException("Attempt to set foreign" + " key to nonexistent table, " |
400 | + currFK.getForeignTableName()); | |
401 | } else { | |
402 | // TODO check type and size | |
403 | 0 | List referrers = foreignTable.getReferrers(); |
404 | 0 | if ((referrers == null || !referrers.contains(currFK))) { |
405 | 0 | foreignTable.addReferrer(currFK); |
406 | } | |
407 | ||
408 | // local column references | |
409 | 0 | Iterator localColumnNames = currFK.getLocalColumns().iterator(); |
410 | 0 | while (localColumnNames.hasNext()) { |
411 | 0 | Column local = currTable.getColumn((String) localColumnNames.next()); |
412 | // give notice of a schema inconsistency. | |
413 | // note we do not prevent the npe as there is nothing | |
414 | // that we can do, if it is to occur. | |
415 | 0 | if (local == null) { |
416 | 0 | throw new EngineException("Attempt to define foreign" |
417 | + " key with nonexistent column in table, " + currTable.getName()); | |
418 | } else { | |
419 | // check for foreign pk's | |
420 | 0 | if (local.isPrimaryKey()) { |
421 | 0 | currTable.setContainsForeignPK(true); |
422 | } | |
423 | } | |
424 | 0 | } |
425 | ||
426 | // foreign column references | |
427 | 0 | Iterator foreignColumnNames = currFK.getForeignColumns().iterator(); |
428 | 0 | while (foreignColumnNames.hasNext()) { |
429 | 0 | String foreignColumnName = (String) foreignColumnNames.next(); |
430 | 0 | Column foreign = foreignTable.getColumn(foreignColumnName); |
431 | // if the foreign column does not exist, we may have an | |
432 | // external reference or a misspelling | |
433 | 0 | if (foreign == null) { |
434 | 0 | throw new EngineException("Attempt to set foreign" + " key to nonexistent column: table=" |
435 | + 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()) |
496 | .append('"').append(" defaultIdMethod=\"").append(getDefaultIdMethod()).append('"') | |
497 | .append(" baseClass=\"").append(getBaseClass()).append('"').append(" basePeer=\"") | |
498 | .append(getBasePeer()).append('"').append(">\n"); | |
499 | ||
500 | 0 | for (Iterator i = tableList.iterator(); i.hasNext();) { |
501 | 0 | result.append(i.next()); |
502 | } | |
503 | ||
504 | 0 | result.append("</database>"); |
505 | 0 | return result.toString(); |
506 | } | |
507 | ||
508 | /** | |
509 | * Add an XML Specified option key/value pair to this element's option set. | |
510 | * | |
511 | * @param key | |
512 | * the key of the option. | |
513 | * @param value | |
514 | * the value of the option. | |
515 | */ | |
516 | public void addOption(String key, String value) { | |
517 | 0 | options.put(key, value); |
518 | 0 | } |
519 | ||
520 | /** | |
521 | * Get the value that was associated with this key in an XML option element. | |
522 | * | |
523 | * @param key | |
524 | * the key of the option. | |
525 | * @return The value for the key or a null. | |
526 | */ | |
527 | public String getOption(String key) { | |
528 | 0 | return (String) options.get(key); |
529 | } | |
530 | ||
531 | /** | |
532 | * Gets the full ordered hashtable array of items specified by XML option statements under this element. | |
533 | * <p> | |
534 | * | |
535 | * Note, this is not thread save but since it's only used for generation which is single threaded, there should be | |
536 | * minimum danger using this in Velocity. | |
537 | * | |
538 | * @return An Map of all options. Will not be null but may be empty. | |
539 | */ | |
540 | public Map getOptions() { | |
541 | 0 | return options; |
542 | } | |
543 | } |