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,v 1.1.6.1 2008-04-01 04:07:48 jkeller Exp $ 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.synchronizedMap(new ListOrderedMap()); 071 072 /** 073 * Creates a new instance for the specified database type. 074 * 075 * @param databaseType 076 * 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 086 * the xml attributes 087 */ 088 public void loadFromXML(Attributes attrib) { 089 setName(attrib.getValue("name")); 090 pkg = attrib.getValue("package"); 091 baseClass = attrib.getValue("baseClass"); 092 basePeer = attrib.getValue("basePeer"); 093 defaultJavaType = attrib.getValue("defaultJavaType"); 094 defaultIdMethod = attrib.getValue("defaultIdMethod"); 095 defaultJavaNamingMethod = attrib.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 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 }