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.Hashtable; 025 import java.util.Iterator; 026 import java.util.List; 027 import java.util.Map; 028 import org.apache.commons.collections.map.ListOrderedMap; 029 import org.apache.commons.lang.StringUtils; 030 031 import org.apache.commons.logging.Log; 032 import org.apache.commons.logging.LogFactory; 033 034 import org.apache.torque.engine.EngineException; 035 036 import org.xml.sax.Attributes; 037 038 /** 039 * Data about a table used in an application. 040 * 041 * @author <a href="mailto:leon@opticode.co.za">Leon Messerschmidt</a> 042 * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a> 043 * @author <a href="mailto:mpoeschl@marmot.at>Martin Poeschl</a> 044 * @author <a href="mailto:jmcnally@collab.net>John McNally</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: Table.java,v 1.1 2007-10-21 07:57:27 abyrne Exp $ 049 */ 050 public class Table implements IDMethod { 051 /** Logging class from commons.logging */ 052 private static Log log = LogFactory.getLog(Table.class); 053 054 // private AttributeListImpl attributes; 055 private List columnList; 056 private List foreignKeys; 057 private List indices; 058 private List unices; 059 private List idMethodParameters; 060 private String name; 061 private String description; 062 private String javaName; 063 private String idMethod; 064 private String javaNamingMethod; 065 private Database tableParent; 066 private List referrers; 067 private List foreignTableNames; 068 private boolean containsForeignPK; 069 private Column inheritanceColumn; 070 private boolean skipSql; 071 private boolean abstractValue; 072 private String alias; 073 private String enterface; 074 private String pkg; 075 private String baseClass; 076 private String basePeer; 077 private Hashtable columnsByName; 078 private Hashtable columnsByJavaName; 079 private boolean needsTransactionInPostgres; 080 private boolean heavyIndexing; 081 private boolean forReferenceOnly; 082 private Map options; 083 084 /** 085 * Default Constructor 086 */ 087 public Table() { 088 this(null); 089 } 090 091 /** 092 * Constructs a table object with a name 093 * 094 * @param name 095 * table name 096 */ 097 public Table(String name) { 098 this.name = name; 099 columnList = new ArrayList(); 100 foreignKeys = new ArrayList(5); 101 indices = new ArrayList(5); 102 unices = new ArrayList(5); 103 columnsByName = new Hashtable(); 104 columnsByJavaName = new Hashtable(); 105 options = Collections.synchronizedMap(new ListOrderedMap()); 106 } 107 108 /** 109 * Load the table object from an xml tag. 110 * 111 * @param attrib 112 * xml attributes 113 * @param defaultIdMethod 114 * defined at db level 115 */ 116 public void loadFromXML(Attributes attrib, String defaultIdMethod) { 117 name = attrib.getValue("name"); 118 javaName = attrib.getValue("javaName"); 119 idMethod = attrib.getValue("idMethod"); 120 121 // retrieves the method for converting from specified name to 122 // a java name. 123 javaNamingMethod = attrib.getValue("javaNamingMethod"); 124 if (javaNamingMethod == null) { 125 javaNamingMethod = getDatabase().getDefaultJavaNamingMethod(); 126 } 127 128 if ("null".equals(idMethod)) { 129 idMethod = defaultIdMethod; 130 } 131 skipSql = "true".equals(attrib.getValue("skipSql")); 132 // pkg = attrib.getValue("package"); 133 abstractValue = "true".equals(attrib.getValue("abstract")); 134 baseClass = attrib.getValue("baseClass"); 135 basePeer = attrib.getValue("basePeer"); 136 alias = attrib.getValue("alias"); 137 heavyIndexing = "true".equals(attrib.getValue("heavyIndexing")) || (!"false".equals(attrib.getValue("heavyIndexing")) && getDatabase().isHeavyIndexing()); 138 description = attrib.getValue("description"); 139 enterface = attrib.getValue("interface"); 140 } 141 142 /** 143 * <p> 144 * A hook for the SAX XML parser to call when this table has been fully loaded from the XML, and all nested elements 145 * have been processed. 146 * </p> 147 * 148 * <p> 149 * Performs heavy indexing and naming of elements which weren't provided with a name. 150 * </p> 151 */ 152 public void doFinalInitialization() { 153 // Heavy indexing must wait until after all columns composing 154 // a table's primary key have been parsed. 155 if (heavyIndexing) { 156 doHeavyIndexing(); 157 } 158 159 // Name any indices which are missing a name using the 160 // appropriate algorithm. 161 doNaming(); 162 } 163 164 /** 165 * <p> 166 * Adds extra indices for multi-part primary key columns. 167 * </p> 168 * 169 * <p> 170 * For databases like MySQL, values in a where clause must match key part order from the left to right. So, in the 171 * key definition <code>PRIMARY KEY (FOO_ID, BAR_ID)</code>, <code>FOO_ID</code> <i>must</i> be the first element 172 * used in the <code>where</code> clause of the SQL query used against this table for the primary key index to be 173 * used. This feature could cause problems under MySQL with heavily indexed tables, as MySQL currently only supports 174 * 16 indices per table (i.e. it might cause too many indices to be created). 175 * </p> 176 * 177 * <p> 178 * See <a href="http://www.mysql.com/doc/E/X/EXPLAIN.html">the manual</a> for a better description of why heavy 179 * indexing is useful for quickly searchable database tables. 180 * </p> 181 */ 182 private void doHeavyIndexing() { 183 if (log.isDebugEnabled()) { 184 log.debug("doHeavyIndex() called on table " + name); 185 } 186 187 List pk = getPrimaryKey(); 188 int size = pk.size(); 189 190 try { 191 // We start at an offset of 1 because the entire column 192 // list is generally implicitly indexed by the fact that 193 // it's a primary key. 194 for (int i = 1; i < size; i++) { 195 addIndex(new Index(this, pk.subList(i, size))); 196 } 197 } catch (EngineException e) { 198 log.error(e, e); 199 } 200 } 201 202 /** 203 * Names composing objects which haven't yet been named. This currently consists of foreign-key and index entities. 204 */ 205 private void doNaming() { 206 int i; 207 int size; 208 String name; 209 210 // Assure names are unique across all databases. 211 try { 212 for (i = 0, size = foreignKeys.size(); i < size; i++) { 213 ForeignKey fk = (ForeignKey) foreignKeys.get(i); 214 name = fk.getName(); 215 if (StringUtils.isEmpty(name)) { 216 name = acquireConstraintName("FK", i + 1); 217 fk.setName(name); 218 } 219 } 220 221 for (i = 0, size = indices.size(); i < size; i++) { 222 Index index = (Index) indices.get(i); 223 name = index.getName(); 224 if (StringUtils.isEmpty(name)) { 225 name = acquireConstraintName("I", i + 1); 226 index.setName(name); 227 } 228 } 229 230 for (i = 0, size = unices.size(); i < size; i++) { 231 Unique unique = (Unique) unices.get(i); 232 name = unique.getName(); 233 if (StringUtils.isEmpty(name)) { 234 name = acquireConstraintName("U", i + 1); 235 unique.setName(name); 236 } 237 } 238 } catch (EngineException nameAlreadyInUse) { 239 log.error(nameAlreadyInUse, nameAlreadyInUse); 240 } 241 } 242 243 /** 244 * Macro to a constraint name. 245 * 246 * @param nameType 247 * constraint type 248 * @param nbr 249 * unique number for this constraint type 250 * @return unique name for constraint 251 * @throws EngineException 252 */ 253 private final String acquireConstraintName(String nameType, int nbr) throws EngineException { 254 List inputs = new ArrayList(4); 255 inputs.add(getDatabase()); 256 inputs.add(getName()); 257 inputs.add(nameType); 258 inputs.add(new Integer(nbr)); 259 return NameFactory.generateName(NameFactory.CONSTRAINT_GENERATOR, inputs); 260 } 261 262 /** 263 * Gets the value of base class for classes produced from this table. 264 * 265 * @return The base class for classes produced from this table. 266 */ 267 public String getBaseClass() { 268 if (isAlias() && baseClass == null) { 269 return alias; 270 } else if (baseClass == null) { 271 return getDatabase().getBaseClass(); 272 } else { 273 return baseClass; 274 } 275 } 276 277 /** 278 * Set the value of baseClass. 279 * 280 * @param v 281 * Value to assign to baseClass. 282 */ 283 public void setBaseClass(String v) { 284 this.baseClass = v; 285 } 286 287 /** 288 * Get the value of basePeer. 289 * 290 * @return value of basePeer. 291 */ 292 public String getBasePeer() { 293 if (isAlias() && basePeer == null) { 294 return alias + "Peer"; 295 } else if (basePeer == null) { 296 return getDatabase().getBasePeer(); 297 } else { 298 return basePeer; 299 } 300 } 301 302 /** 303 * Set the value of basePeer. 304 * 305 * @param v 306 * Value to assign to basePeer. 307 */ 308 public void setBasePeer(String v) { 309 this.basePeer = v; 310 } 311 312 /** 313 * A utility function to create a new column from attrib and add it to this table. 314 * 315 * @param attrib 316 * xml attributes for the column to add 317 * @return the added column 318 */ 319 public Column addColumn(Attributes attrib) { 320 Column col = new Column(); 321 col.setTable(this); 322 col.setCorrectGetters(false); 323 col.loadFromXML(attrib); 324 addColumn(col); 325 return col; 326 } 327 328 /** 329 * Adds a new column to the column list and set the parent table of the column to the current table 330 * 331 * @param col 332 * the column to add 333 */ 334 public void addColumn(Column col) { 335 col.setTable(this); 336 if (col.isInheritance()) { 337 inheritanceColumn = col; 338 } 339 columnList.add(col); 340 columnsByName.put(col.getName(), col); 341 columnsByJavaName.put(col.getJavaName(), col); 342 col.setPosition(columnList.size()); 343 needsTransactionInPostgres |= col.requiresTransactionInPostgres(); 344 } 345 346 /** 347 * A utility function to create a new foreign key from attrib and add it to this table. 348 * 349 * @param attrib 350 * the xml attributes 351 * @return the created ForeignKey 352 */ 353 public ForeignKey addForeignKey(Attributes attrib) { 354 ForeignKey fk = new ForeignKey(); 355 fk.loadFromXML(attrib); 356 addForeignKey(fk); 357 return fk; 358 } 359 360 /** 361 * Gets the column that subclasses of the class representing this table can be produced from. 362 */ 363 public Column getChildrenColumn() { 364 return inheritanceColumn; 365 } 366 367 /** 368 * Get the objects that can be created from this table. 369 */ 370 public List getChildrenNames() { 371 if (inheritanceColumn == null || !inheritanceColumn.isEnumeratedClasses()) { 372 return null; 373 } 374 List children = inheritanceColumn.getChildren(); 375 List names = new ArrayList(children.size()); 376 for (int i = 0; i < children.size(); i++) { 377 names.add(((Inheritance) children.get(i)).getClassName()); 378 } 379 return names; 380 } 381 382 /** 383 * Adds the foreign key from another table that refers to this table. 384 * 385 * @param fk 386 * A foreign key refering to this table 387 */ 388 public void addReferrer(ForeignKey fk) { 389 if (referrers == null) { 390 referrers = new ArrayList(5); 391 } 392 referrers.add(fk); 393 } 394 395 /** 396 * Get list of references to this table. 397 * 398 * @return A list of references to this table 399 */ 400 public List getReferrers() { 401 return referrers; 402 } 403 404 /** 405 * Set whether this table contains a foreign PK 406 * 407 * @param b 408 */ 409 public void setContainsForeignPK(boolean b) { 410 containsForeignPK = b; 411 } 412 413 /** 414 * Determine if this table contains a foreign PK 415 */ 416 public boolean getContainsForeignPK() { 417 return containsForeignPK; 418 } 419 420 /** 421 * A list of tables referenced by foreign keys in this table 422 * 423 * @return A list of tables 424 */ 425 public List getForeignTableNames() { 426 if (foreignTableNames == null) { 427 foreignTableNames = new ArrayList(1); 428 } 429 return foreignTableNames; 430 } 431 432 /** 433 * Adds a new FK to the FK list and set the parent table of the column to the current table 434 * 435 * @param fk 436 * A foreign key 437 */ 438 public void addForeignKey(ForeignKey fk) { 439 fk.setTable(this); 440 foreignKeys.add(fk); 441 442 if (foreignTableNames == null) { 443 foreignTableNames = new ArrayList(5); 444 } 445 if (!foreignTableNames.contains(fk.getForeignTableName())) { 446 foreignTableNames.add(fk.getForeignTableName()); 447 } 448 } 449 450 /** 451 * Return true if the column requires a transaction in Postgres 452 */ 453 public boolean requiresTransactionInPostgres() { 454 return needsTransactionInPostgres; 455 } 456 457 /** 458 * A utility function to create a new id method parameter from attrib and add it to this table. 459 */ 460 public IdMethodParameter addIdMethodParameter(Attributes attrib) { 461 IdMethodParameter imp = new IdMethodParameter(); 462 imp.loadFromXML(attrib); 463 addIdMethodParameter(imp); 464 return imp; 465 } 466 467 /** 468 * Adds a new ID method parameter to the list and sets the parent table of the column associated with the supplied 469 * parameter to this table. 470 * 471 * @param imp 472 * The column to add as an ID method parameter. 473 */ 474 public void addIdMethodParameter(IdMethodParameter imp) { 475 imp.setTable(this); 476 if (idMethodParameters == null) { 477 idMethodParameters = new ArrayList(2); 478 } 479 idMethodParameters.add(imp); 480 } 481 482 /** 483 * Adds a new index to the index list and set the parent table of the column to the current table 484 */ 485 public void addIndex(Index index) { 486 index.setTable(this); 487 indices.add(index); 488 } 489 490 /** 491 * A utility function to create a new index from attrib and add it to this table. 492 */ 493 public Index addIndex(Attributes attrib) { 494 Index index = new Index(); 495 index.loadFromXML(attrib); 496 addIndex(index); 497 return index; 498 } 499 500 /** 501 * Adds a new Unique to the Unique list and set the parent table of the column to the current table 502 */ 503 public void addUnique(Unique unique) { 504 unique.setTable(this); 505 unices.add(unique); 506 } 507 508 /** 509 * A utility function to create a new Unique from attrib and add it to this table. 510 * 511 * @param attrib 512 * the xml attributes 513 */ 514 public Unique addUnique(Attributes attrib) { 515 Unique unique = new Unique(); 516 unique.loadFromXML(attrib); 517 addUnique(unique); 518 return unique; 519 } 520 521 /** 522 * Get the name of the Table 523 */ 524 public String getName() { 525 return name; 526 } 527 528 /** 529 * Set the name of the Table 530 */ 531 public void setName(String newName) { 532 name = newName; 533 } 534 535 /** 536 * Get the description for the Table 537 */ 538 public String getDescription() { 539 return description; 540 } 541 542 /** 543 * Set the description for the Table 544 * 545 * @param newDescription 546 * description for the Table 547 */ 548 public void setDescription(String newDescription) { 549 description = newDescription; 550 } 551 552 /** 553 * Get name to use in Java sources 554 */ 555 public String getJavaName() { 556 if (javaName == null) { 557 List inputs = new ArrayList(2); 558 inputs.add(name); 559 inputs.add(javaNamingMethod); 560 try { 561 javaName = NameFactory.generateName(NameFactory.JAVA_GENERATOR, inputs); 562 } catch (EngineException e) { 563 log.error(e, e); 564 } 565 } 566 return javaName; 567 } 568 569 /** 570 * Set name to use in Java sources 571 */ 572 public void setJavaName(String javaName) { 573 this.javaName = javaName; 574 } 575 576 /** 577 * Get the method for generating pk's 578 */ 579 public String getIdMethod() { 580 if (idMethod == null) { 581 return IDMethod.NO_ID_METHOD; 582 } else { 583 return idMethod; 584 } 585 } 586 587 /** 588 * Set the method for generating pk's 589 */ 590 public void setIdMethod(String idMethod) { 591 this.idMethod = idMethod; 592 } 593 594 /** 595 * Skip generating sql for this table (in the event it should not be created from scratch). 596 * 597 * @return value of skipSql. 598 */ 599 public boolean isSkipSql() { 600 return (skipSql || isAlias() || isForReferenceOnly()); 601 } 602 603 /** 604 * Set whether this table should have its creation sql generated. 605 * 606 * @param v 607 * Value to assign to skipSql. 608 */ 609 public void setSkipSql(boolean v) { 610 this.skipSql = v; 611 } 612 613 /** 614 * JavaName of om object this entry references. 615 * 616 * @return value of external. 617 */ 618 public String getAlias() { 619 return alias; 620 } 621 622 /** 623 * Is this table specified in the schema or is there just a foreign key reference to it. 624 * 625 * @return value of external. 626 */ 627 public boolean isAlias() { 628 return (alias != null); 629 } 630 631 /** 632 * Set whether this table specified in the schema or is there just a foreign key reference to it. 633 * 634 * @param v 635 * Value to assign to alias. 636 */ 637 public void setAlias(String v) { 638 this.alias = v; 639 } 640 641 /** 642 * Interface which objects for this table will implement 643 * 644 * @return value of interface. 645 */ 646 public String getInterface() { 647 return enterface; 648 } 649 650 /** 651 * Interface which objects for this table will implement 652 * 653 * @param v 654 * Value to assign to interface. 655 */ 656 public void setInterface(String v) { 657 this.enterface = v; 658 } 659 660 /** 661 * When a table is abstract, it marks the business object class that is generated as being abstract. If you have a 662 * table called "FOO", then the Foo BO will be <code>public abstract class Foo</code> This helps support class 663 * hierarchies 664 * 665 * @return value of abstractValue. 666 */ 667 public boolean isAbstract() { 668 return abstractValue; 669 } 670 671 /** 672 * When a table is abstract, it marks the business object class that is generated as being abstract. If you have a 673 * table called "FOO", then the Foo BO will be <code>public abstract class Foo</code> This helps support class 674 * hierarchies 675 * 676 * @param v 677 * Value to assign to abstractValue. 678 */ 679 public void setAbstract(boolean v) { 680 this.abstractValue = v; 681 } 682 683 /** 684 * Get the value of package. 685 * 686 * @return value of package. 687 */ 688 public String getPackage() { 689 if (pkg != null) { 690 return pkg; 691 } else { 692 return this.getDatabase().getPackage(); 693 } 694 } 695 696 /** 697 * Set the value of package. 698 * 699 * @param v 700 * Value to assign to package. 701 */ 702 public void setPackage(String v) { 703 this.pkg = v; 704 } 705 706 /** 707 * Returns a List containing all the columns in the table 708 * 709 * @return a List containing all the columns 710 */ 711 public List getColumns() { 712 return columnList; 713 } 714 715 /** 716 * Utility method to get the number of columns in this table 717 */ 718 public int getNumColumns() { 719 return columnList.size(); 720 } 721 722 /** 723 * Returns a List containing all the FKs in the table 724 * 725 * @return a List containing all the FKs 726 */ 727 public List getForeignKeys() { 728 return foreignKeys; 729 } 730 731 /** 732 * Returns a Collection of parameters relevant for the chosen id generation method. 733 */ 734 public List getIdMethodParameters() { 735 return idMethodParameters; 736 } 737 738 /** 739 * A name to use for creating a sequence if one is not specified. 740 * 741 * @return name of the sequence 742 */ 743 public String getSequenceName() { 744 String result = null; 745 if (getIdMethod().equals(NATIVE)) { 746 List idMethodParams = getIdMethodParameters(); 747 if (idMethodParams == null) { 748 result = getName() + "_SEQ"; 749 } else { 750 result = ((IdMethodParameter) idMethodParams.get(0)).getValue(); 751 } 752 } 753 return result; 754 } 755 756 /** 757 * Returns a List containing all the indices in the table 758 * 759 * @return A List containing all the indices 760 */ 761 public List getIndices() { 762 return indices; 763 } 764 765 /** 766 * Returns a List containing all the UKs in the table 767 * 768 * @return A List containing all the UKs 769 */ 770 public List getUnices() { 771 return unices; 772 } 773 774 /** 775 * Returns a specified column. 776 * 777 * @param name 778 * name of the column 779 * @return Return a Column object or null if it does not exist. 780 */ 781 public Column getColumn(String name) { 782 return (Column) columnsByName.get(name); 783 } 784 785 /** 786 * Returns a specified column. 787 * 788 * @param javaName 789 * java name of the column 790 * @return Return a Column object or null if it does not exist. 791 */ 792 public Column getColumnByJavaName(String javaName) { 793 return (Column) columnsByJavaName.get(javaName); 794 } 795 796 /** 797 * Return the first foreign key that includes col in it's list of local columns. Eg. Foreign key (a,b,c) refrences 798 * tbl(x,y,z) will be returned of col is either a,b or c. 799 * 800 * @param col 801 * column name included in the key 802 * @return Return a Column object or null if it does not exist. 803 */ 804 public ForeignKey getForeignKey(String col) { 805 ForeignKey firstFK = null; 806 for (Iterator iter = foreignKeys.iterator(); iter.hasNext();) { 807 ForeignKey key = (ForeignKey) iter.next(); 808 if (key.getLocalColumns().contains(col)) { 809 if (firstFK == null) { 810 firstFK = key; 811 } else { 812 // System.out.println(col+" is in multiple FKs. This is not" 813 // + " being handled properly."); 814 // throw new IllegalStateException("Cannot call method if " + 815 // "column is referenced multiple times"); 816 } 817 } 818 } 819 return firstFK; 820 } 821 822 /** 823 * Returns true if the table contains a specified column 824 * 825 * @param col 826 * the column 827 * @return true if the table contains the column 828 */ 829 public boolean containsColumn(Column col) { 830 return columnList.contains(col); 831 } 832 833 /** 834 * Returns true if the table contains a specified column 835 * 836 * @param name 837 * name of the column 838 * @return true if the table contains the column 839 */ 840 public boolean containsColumn(String name) { 841 return (getColumn(name) != null); 842 } 843 844 /** 845 * Set the parent of the table 846 * 847 * @param parent 848 * the parant database 849 */ 850 public void setDatabase(Database parent) { 851 tableParent = parent; 852 } 853 854 /** 855 * Get the parent of the table 856 * 857 * @return the parant database 858 */ 859 public Database getDatabase() { 860 return tableParent; 861 } 862 863 /** 864 * Flag to determine if code/sql gets created for this table. Table will be skipped, if return true. 865 * 866 * @return value of forReferenceOnly. 867 */ 868 public boolean isForReferenceOnly() { 869 return forReferenceOnly; 870 } 871 872 /** 873 * Flag to determine if code/sql gets created for this table. Table will be skipped, if set to true. 874 * 875 * @param v 876 * Value to assign to forReferenceOnly. 877 */ 878 public void setForReferenceOnly(boolean v) { 879 this.forReferenceOnly = v; 880 } 881 882 /** 883 * Returns a XML representation of this table. 884 * 885 * @return XML representation of this table 886 */ 887 public String toString() { 888 StringBuffer result = new StringBuffer(); 889 890 result.append("<table name=\"").append(name).append('\"'); 891 892 if (javaName != null) { 893 result.append(" javaName=\"").append(javaName).append('\"'); 894 } 895 896 if (idMethod != null) { 897 result.append(" idMethod=\"").append(idMethod).append('\"'); 898 } 899 900 if (skipSql) { 901 result.append(" skipSql=\"").append(new Boolean(skipSql)).append('\"'); 902 } 903 904 if (abstractValue) { 905 result.append(" abstract=\"").append(new Boolean(abstractValue)).append('\"'); 906 } 907 908 if (baseClass != null) { 909 result.append(" baseClass=\"").append(baseClass).append('\"'); 910 } 911 912 if (basePeer != null) { 913 result.append(" basePeer=\"").append(basePeer).append('\"'); 914 } 915 916 result.append(">\n"); 917 918 if (columnList != null) { 919 for (Iterator iter = columnList.iterator(); iter.hasNext();) { 920 result.append(iter.next()); 921 } 922 } 923 924 if (foreignKeys != null) { 925 for (Iterator iter = foreignKeys.iterator(); iter.hasNext();) { 926 result.append(iter.next()); 927 } 928 } 929 930 if (idMethodParameters != null) { 931 Iterator iter = idMethodParameters.iterator(); 932 while (iter.hasNext()) { 933 result.append(iter.next()); 934 } 935 } 936 937 result.append("</table>\n"); 938 939 return result.toString(); 940 } 941 942 /** 943 * Returns the collection of Columns which make up the single primary key for this table. 944 * 945 * @return A list of the primary key parts. 946 */ 947 public List getPrimaryKey() { 948 List pk = new ArrayList(columnList.size()); 949 950 Iterator iter = columnList.iterator(); 951 while (iter.hasNext()) { 952 Column col = (Column) iter.next(); 953 if (col.isPrimaryKey()) { 954 pk.add(col); 955 } 956 } 957 return pk; 958 } 959 960 /** 961 * Determine whether this table has a primary key. 962 * 963 * @return Whether this table has any primary key parts. 964 */ 965 public boolean hasPrimaryKey() { 966 return (getPrimaryKey().size() > 0); 967 } 968 969 /** 970 * Returns all parts of the primary key, separated by commas. 971 * 972 * @return A CSV list of primary key parts. 973 */ 974 public String printPrimaryKey() { 975 return printList(columnList); 976 } 977 978 /** 979 * Returns the elements of the list, separated by commas. 980 * 981 * @param list 982 * a list of Columns 983 * @return A CSV list. 984 */ 985 private String printList(List list) { 986 StringBuffer result = new StringBuffer(); 987 boolean comma = false; 988 for (Iterator iter = list.iterator(); iter.hasNext();) { 989 Column col = (Column) iter.next(); 990 if (col.isPrimaryKey()) { 991 if (comma) { 992 result.append(','); 993 } else { 994 comma = true; 995 } 996 result.append(col.getName()); 997 } 998 } 999 return result.toString(); 1000 } 1001 1002 /** 1003 * Force all columns to set the correctGetters property. 1004 * 1005 * @param value 1006 * The new value of the correctGetters property. 1007 * @since 3.2 1008 */ 1009 public void setCorrectGetters(Boolean value) { 1010 boolean correctGetters = value != null && value.booleanValue(); 1011 for (Iterator it = columnList.iterator(); it.hasNext();) { 1012 Column col = (Column) it.next(); 1013 col.setCorrectGetters(correctGetters); 1014 } 1015 } 1016 1017 /** 1018 * Add an XML Specified option key/value pair to this element's option set. 1019 * 1020 * @param key 1021 * the key of the option. 1022 * @param value 1023 * the value of the option. 1024 */ 1025 public void addOption(String key, String value) { 1026 options.put(key, value); 1027 } 1028 1029 /** 1030 * Get the value that was associated with this key in an XML option element. 1031 * 1032 * @param key 1033 * the key of the option. 1034 * @return The value for the key or a null. 1035 */ 1036 public String getOption(String key) { 1037 return (String) options.get(key); 1038 } 1039 1040 /** 1041 * Gets the full ordered hashtable array of items specified by XML option statements under this element. 1042 * <p> 1043 * 1044 * Note, this is not thread save but since it's only used for generation which is single threaded, there should be 1045 * minimum danger using this in Velocity. 1046 * 1047 * @return An Map of all options. Will not be null but may be empty. 1048 */ 1049 public Map getOptions() { 1050 return options; 1051 } 1052 }