/*
 *  Copyright 2008 biaoping.yin
 *
 *  Licensed under the Apache License, Version 2.0 (the "License");
 *  you may not use this file except in compliance with the License.
 *  You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *  Unless required by applicable law or agreed to in writing, software
 *  distributed under the License is distributed on an "AS IS" BASIS,
 *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *  See the License for the specific language governing permissions and
 *  limitations under the License.  
 */
package com.frameworkset.orm.engine.model;

/*
 
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;

import org.apache.commons.lang.StringUtils;
import org.apache.log4j.Logger;
import org.xml.sax.Attributes;

import com.frameworkset.orm.engine.EngineException;

/**
 * Data about a table used in an application.
 *
 * @author <a href="mailto:leon@opticode.co.za">Leon Messerschmidt</a>
 * @author <a href="mailto:jvanzyl@apache.org">Jason van Zyl</a>
 * @author <a href="mailto:mpoeschl@marmot.at>Martin Poeschl</a>
 * @author <a href="mailto:jmcnally@collab.net>John McNally</a>
 * @author <a href="mailto:dlr@collab.net>Daniel Rall</a>
 * @author <a href="mailto:byron_foster@byron_foster@yahoo.com>Byron Foster</a>
 * @version $Id: Table.java,v 1.9 2005/01/31 19:43:56 tfischer Exp $
 */ 
public class Table implements IDMethod,Comparable,Serializable
{
    /** Logging class from commons.logging */
    private static Logger log = Logger.getLogger(Table.class);

    //private AttributeListImpl attributes;
    private List columnList;
    private List foreignKeys;
    private List indices;
    private List unices;
    private List idMethodParameters;
    private String name;
    private String description;
    private String javaName;
    private String idMethod;
    private String javaNamingMethod;
    private Database tableParent;
    private List referrers;
    private List foreignTableNames;
    private boolean containsForeignPK;
    private Column inheritanceColumn;
    private boolean skipSql;
    private boolean abstractValue;
    private String alias;
    private String enterface;
    private String pkg;
    private String baseClass;
    private String basePeer;
    private Hashtable columnsByName;
    private Hashtable columnsByJavaName;
    private boolean needsTransactionInPostgres;
    private boolean heavyIndexing;
    private boolean forReferenceOnly;


    /**
     * Default Constructor
     */
    public Table()
    {
        this(null);
    }

    /**
     * Constructs a table object with a name
     *
     * @param name table name
     */
    public Table(String name)
    {
        this.name = (name == null)?"":name;
        columnList = new ArrayList();
        foreignKeys = new ArrayList(5);
        indices = new ArrayList(5);
        unices = new ArrayList(5);
        columnsByName = new Hashtable();
        columnsByJavaName = new Hashtable();
    }

    /**
     * Load the table object from an xml tag.
     *
     * @param attrib xml attributes
     * @param defaultIdMethod defined at db level
     */
    public void loadFromXML(Attributes attrib, String defaultIdMethod)
    {
        name = attrib.getValue("name");
        javaName = attrib.getValue("javaName");
        idMethod = attrib.getValue("idMethod");

        // retrieves the method for converting from specified name to
        // a java name.
        javaNamingMethod = attrib.getValue("javaNamingMethod");
        if (javaNamingMethod == null)
        {
            javaNamingMethod = getDatabase().getDefaultJavaNamingMethod();
        }

        if ("null".equals(idMethod))
        {
            idMethod = defaultIdMethod;
        }
        skipSql = "true".equals(attrib.getValue("skipSql"));
        // pkg = attrib.getValue("package");
        abstractValue = "true".equals(attrib.getValue("abstract"));
        baseClass = attrib.getValue("baseClass");
        basePeer = attrib.getValue("basePeer");
        alias = attrib.getValue("alias");
        heavyIndexing = "true".equals(attrib.getValue("heavyIndexing"))
                || (!"false".equals(attrib.getValue("heavyIndexing"))
                && getDatabase().isHeavyIndexing());
        description = attrib.getValue("description");
        enterface = attrib.getValue("interface");
    }

    public boolean equals(Object table)
    {
        boolean ret = false;
        if(!(table instanceof Table))
        {
            ret = false;
        }
        Table temp = (Table)table;
//        log.debug("temp.getName():"+temp.getName());
//        log.debug("this.getName():"+this.getName());
//        log.debug("temp.getJavaName():"+temp.getJavaName());
//        log.debug("this.getJavaName():"+this.getJavaName());
        if(temp.getName() == null && this.getName() == null )
        {
            if (temp.getJavaName().equalsIgnoreCase(getJavaName()))
                ret = true;
        }
        else if((temp.getName() != null && this.getName() != null)
                && temp.getName().equalsIgnoreCase(this.getName())
                && temp.getJavaName().equalsIgnoreCase(getJavaName()))
            ret = true;
        //log.debug("ret:"+ret);
        return ret;
    }
    /**
     * <p>A hook for the SAX XML parser to call when this table has
     * been fully loaded from the XML, and all nested elements have
     * been processed.</p>
     *
     * <p>Performs heavy indexing and naming of elements which weren't
     * provided with a name.</p>
     */
    public void doFinalInitialization()
    {
        // Heavy indexing must wait until after all columns composing
        // a table's primary key have been parsed.
        if (heavyIndexing)
        {
            doHeavyIndexing();
        }

        // Name any indices which are missing a name using the
        // appropriate algorithm.
        doNaming();
    }

    /**
     * <p>Adds extra indices for multi-part primary key columns.</p>
     *
     * <p>For databases like MySQL, values in a where clause must
     * match key part order from the left to right.  So, in the key
     * definition <code>PRIMARY KEY (FOO_ID, BAR_ID)</code>,
     * <code>FOO_ID</code> <i>must</i> be the first element used in
     * the <code>where</code> clause of the SQL query used against
     * this table for the primary key index to be used.  This feature
     * could cause problems under MySQL with heavily indexed tables,
     * as MySQL currently only supports 16 indices per table (i.e. it
     * might cause too many indices to be created).</p>
     *
     * <p>See <a href="http://www.mysql.com/doc/E/X/EXPLAIN.html">the
     * manual</a> for a better description of why heavy indexing is
     * useful for quickly searchable database tables.</p>
     */
    private void doHeavyIndexing()
    {
        if (log.isDebugEnabled())
        {
            log.debug("doHeavyIndex() called on table " + name);
        }

        List pk = getPrimaryKey();
        int size = pk.size();

        try
        {
            // We start at an offset of 1 because the entire column
            // list is generally implicitly indexed by the fact that
            // it's a primary key.
            for (int i = 1; i < size; i++)
            {
                addIndex(new Index(this, pk.subList(i, size)));
            }
        }
        catch (EngineException e)
        {
            log.error(e, e);
        }
    }

    /**
     * Names composing objects which haven't yet been named.  This
     * currently consists of foreign-key and index entities.
     */
    private void doNaming()
    {
        int i;
        int size;
        String name;

        // Assure names are unique across all databases.
        try
        {
            for (i = 0, size = foreignKeys.size(); i < size; i++)
            {
                ForeignKey fk = (ForeignKey) foreignKeys.get(i);
                name = fk.getName();
                if (StringUtils.isEmpty(name))
                {
                    name = acquireConstraintName("FK", i + 1);
                    fk.setName(name);
                }
            }

            for (i = 0, size = indices.size(); i < size; i++)
            {
                Index index = (Index) indices.get(i);
                name = index.getName();
                if (StringUtils.isEmpty(name))
                {
                    name = acquireConstraintName("I", i + 1);
                    index.setName(name);
                }
            }

            for (i = 0, size = unices.size(); i < size; i++)
            {
                Unique unique = (Unique) unices.get(i);
                name = unique.getName();
                if (StringUtils.isEmpty(name))
                {
                    name = acquireConstraintName("U", i + 1);
                    unique.setName(name);
                }
            }
        }
        catch (EngineException nameAlreadyInUse)
        {
            log.error(nameAlreadyInUse, nameAlreadyInUse);
        }
    }

    /**
     * Macro to a constraint name.
     *
     * @param nameType constraint type
     * @param nbr unique number for this constraint type
     * @return unique name for constraint
     * @throws EngineException
     */
    private final String acquireConstraintName(String nameType, int nbr)
            throws EngineException
    {
        List inputs = new ArrayList(4);
        inputs.add(getDatabase());
        inputs.add(getName());
        inputs.add(nameType);
        inputs.add(new Integer(nbr));
        return NameFactory.generateName(NameFactory.CONSTRAINT_GENERATOR,
                                        inputs,false);
    }

    /**
     * Gets the value of base class for classes produced from this table.
     *
     * @return The base class for classes produced from this table.
     */
    public String getBaseClass()
    {
        if (isAlias() && baseClass == null)
        {
            return alias;
        }
        else if (baseClass == null)
        {
            return getDatabase().getBaseClass();
        }
        else
        {
            return baseClass;
        }
    }

    /**
     * Set the value of baseClass.
     * @param v  Value to assign to baseClass.
     */
    public void setBaseClass(String v)
    {
        this.baseClass = v;
    }

    /**
     * Get the value of basePeer.
     * @return value of basePeer.
     */
    public String getBasePeer()
    {
        if (isAlias() && basePeer == null)
        {
            return alias + "Peer";
        }
        else if (basePeer == null)
        {
            return getDatabase().getBasePeer();
        }
        else
        {
            return basePeer;
        }
    }

    /**
     * Set the value of basePeer.
     * @param v  Value to assign to basePeer.
     */
    public void setBasePeer(String v)
    {
        this.basePeer = v;
    }

    /**
     * A utility function to create a new column from attrib and add it to this
     * table.
     *
     * @param attrib xml attributes for the column to add
     * @return the added column
     */
    public Column addColumn(Attributes attrib)
    {
        Column col = new Column();
        col.setTable(this);
        col.setCorrectGetters(false);
        col.loadFromXML(attrib);
        addColumn(col);
        return col;
    }

    /**
     * Adds a new column to the column list and set the
     * parent table of the column to the current table
     *
     * @param col the column to add
     */
    public void addColumn(Column col)
    {
        col.setTable (this);
        if (col.isInheritance())
        {
            inheritanceColumn = col;
        }
        columnList.add(col);
        columnsByName.put(col.getName(), col);
        columnsByJavaName.put(col.getJavaName(), col);
        col.setPosition(columnList.size());
        needsTransactionInPostgres |= col.requiresTransactionInPostgres();
    }

    /**
     * A utility function to create a new foreign key
     * from attrib and add it to this table.
     *
     * @param attrib the xml attributes
     * @return the created ForeignKey
     */
    public ForeignKey addForeignKey(Attributes attrib)
    {
        ForeignKey fk = new ForeignKey();
        fk.loadFromXML(attrib);
        addForeignKey(fk);
        return fk;
    }

    /**
     * Gets the column that subclasses of the class representing this
     * table can be produced from.
     */
    public Column getChildrenColumn()
    {
        return inheritanceColumn;
    }

    /**
     * Get the objects that can be created from this table.
     */
    public List getChildrenNames()
    {
        if (inheritanceColumn == null
                || !inheritanceColumn.isEnumeratedClasses())
        {
            return null;
        }
        List children = inheritanceColumn.getChildren();
        List names = new ArrayList(children.size());
        for (int i = 0; i < children.size(); i++)
        {
            names.add(((Inheritance) children.get(i)).getClassName());
        }
        return names;
    }

    /**
     * Adds the foreign key from another table that refers to this table.
     *
     * @param fk A foreign key refering to this table
     */
    public void addReferrer(ForeignKey fk)
    {
        if (referrers == null)
        {
            referrers = new ArrayList(5);
        }
        referrers.add(fk);
    }

    /**
     * Get list of references to this table.
     *
     * @return A list of references to this table
     */
    public List getReferrers()
    {
        return referrers;
    }

    /**
     * Set whether this table contains a foreign PK
     *
     * @param b
     */
    public void setContainsForeignPK(boolean b)
    {
        containsForeignPK = b;
    }

    /**
     * Determine if this table contains a foreign PK
     */
    public boolean getContainsForeignPK()
    {
        return containsForeignPK;
    }

    /**
     * A list of tables referenced by foreign keys in this table
     *
     * @return A list of tables
     */
    public List getForeignTableNames()
    {
        if (foreignTableNames == null)
        {
            foreignTableNames = new ArrayList(1);
        }
        return foreignTableNames;
    }

    /**
     * Adds a new FK to the FK list and set the
     * parent table of the column to the current table
     *
     * @param fk A foreign key
     */
    public void addForeignKey(ForeignKey fk)
    {
        fk.setTable (this);
        foreignKeys.add(fk);

        if (foreignTableNames == null)
        {
            foreignTableNames = new ArrayList(5);
        }
        if (!foreignTableNames.contains(fk.getForeignTableName()))
        {
            foreignTableNames.add(fk.getForeignTableName());
        }
    }

    /**
     * Return true if the column requires a transaction in Postgres
     */
    public boolean requiresTransactionInPostgres()
    {
        return needsTransactionInPostgres;
    }

    /**
     * A utility function to create a new id method parameter
     * from attrib and add it to this table.
     */
    public IdMethodParameter addIdMethodParameter(Attributes attrib)
    {
        IdMethodParameter imp = new IdMethodParameter();
        imp.loadFromXML(attrib);
        addIdMethodParameter(imp);
        return imp;
    }


    /**
     * Adds a new ID method parameter to the list and sets the parent
     * table of the column associated with the supplied parameter to this table.
     *
     * @param imp The column to add as an ID method parameter.
     */
    public void addIdMethodParameter(IdMethodParameter imp)
    {
        imp.setTable(this);
        if (idMethodParameters == null)
        {
            idMethodParameters = new ArrayList(2);
        }
        idMethodParameters.add(imp);
    }

    /**
     * Adds a new index to the index list and set the
     * parent table of the column to the current table
     */
    public void addIndex(Index index)
    {
        index.setTable (this);
        indices.add(index);
    }

    /**
     * A utility function to create a new index
     * from attrib and add it to this table.
     */
    public Index addIndex(Attributes attrib)
    {
        Index index = new Index();
        index.loadFromXML(attrib);
        addIndex(index);
        return index;
    }

    /**
     * Adds a new Unique to the Unique list and set the
     * parent table of the column to the current table
     */
    public void addUnique(Unique unique)
    {
        unique.setTable(this);
        unices.add(unique);
    }

    /**
     * A utility function to create a new Unique
     * from attrib and add it to this table.
     *
     * @param attrib the xml attributes
     */
    public Unique addUnique(Attributes attrib)
    {
        Unique unique = new Unique();
        unique.loadFromXML(attrib);
        addUnique(unique);
        return unique;
    }

    /**
     * Get the name of the Table
     */
    public String getName()
    {
        return name;
    }

    /**
     * Set the name of the Table
     */
    public void setName(String newName)
    {
        name = newName;
    }

    /**
     * Get the description for the Table
     */
    public String getDescription()
    {
        return description;
    }

    /**
     * Set the description for the Table
     *
     * @param newDescription description for the Table
     */
    public void setDescription(String newDescription)
    {
        description = newDescription;
    }

    /**
     * Get name to use in Java sources
     */
    public String getJavaName()
    {
        if (javaName == null)
        {
            List inputs = new ArrayList(2);
            inputs.add(name);
            inputs.add(javaNamingMethod);
            try
            {
                javaName = NameFactory.generateName(NameFactory.JAVA_GENERATOR,
                                                    inputs,false);
            }
            catch (EngineException e)
            {
                log.error(e.getMessage(), e);
            }
        }
        return javaName;
    }

    /**
     * Set name to use in Java sources
     */
    public void setJavaName(String javaName)
    {
        this.javaName = javaName;
    }

    /**
     * Get the method for generating pk's
     */
    public String getIdMethod()
    {
        if (idMethod == null)
        {
            return IDMethod.NO_ID_METHOD;
        }
        else
        {
            return idMethod;
        }
    }

    /**
     * Set the method for generating pk's
     */
    public void setIdMethod(String idMethod)
    {
        this.idMethod = idMethod;
    }

    /**
     * Skip generating sql for this table (in the event it should
     * not be created from scratch).
     * @return value of skipSql.
     */
    public boolean isSkipSql()
    {
        return (skipSql || isAlias() || isForReferenceOnly());
    }

    /**
     * Set whether this table should have its creation sql generated.
     * @param v  Value to assign to skipSql.
     */
    public void setSkipSql(boolean  v)
    {
        this.skipSql = v;
    }

    /**
     * JavaName of om object this entry references.
     * @return value of external.
     */
    public String getAlias()
    {
        return alias;
    }

    /**
     * Is this table specified in the schema or is there just
     * a foreign key reference to it.
     * @return value of external.
     */
    public boolean isAlias()
    {
        return (alias != null);
    }

    /**
     * Set whether this table specified in the schema or is there just
     * a foreign key reference to it.
     * @param v  Value to assign to alias.
     */
    public void setAlias(String v)
    {
        this.alias = v;
    }


    /**
     * Interface which objects for this table will implement
     * @return value of interface.
     */
    public String getInterface()
    {
        return enterface;
    }

    /**
     * Interface which objects for this table will implement
     * @param v  Value to assign to interface.
     */
    public void setInterface(String  v)
    {
        this.enterface = v;
    }

    /**
     * When a table is abstract, it marks the business object class that is
     * generated as being abstract. If you have a table called "FOO", then the
     * Foo BO will be <code>public abstract class Foo</code>
     * This helps support class hierarchies
     *
     * @return value of abstractValue.
     */
    public boolean isAbstract()
    {
        return abstractValue;
    }

    /**
     * When a table is abstract, it marks the business object
     * class that is generated as being abstract. If you have a
     * table called "FOO", then the Foo BO will be
     * <code>public abstract class Foo</code>
     * This helps support class hierarchies
     *
     * @param v  Value to assign to abstractValue.
     */
    public void setAbstract(boolean  v)
    {
        this.abstractValue = v;
    }

    /**
     * Get the value of package.
     *
     * @return value of package.
     */
    public String getPackage()
    {
        if (pkg != null)
        {
            return pkg;
        }
        else
        {
            return this.getDatabase().getPackage();
        }
    }

    /**
     * Set the value of package.
     *
     * @param v  Value to assign to package.
     */
    public void setPackage(String v)
    {
        this.pkg = v;
    }

    /**
     * Returns a List containing all the columns in the table
     *
     * @return a List containing all the columns
     */
    public List getColumns()
    {
        return columnList;
    }

    /**
     * Utility method to get the number of columns in this table
     */
    public int getNumColumns()
    {
        return columnList.size();
    }

    /**
     * Returns a List containing all the FKs in the table
     *
     * @return a List containing all the FKs
     */
    public List getForeignKeys()
    {
        return foreignKeys;
    }

    /**
     * Returns a Collection of parameters relevant for the chosen
     * id generation method.
     */
    public List getIdMethodParameters()
    {
        return idMethodParameters;
    }

    /**
     * A name to use for creating a sequence if one is not specified.
     *
     * @return name of the sequence
     */
    public String getSequenceName()
    {
        String result = null;
        if (getIdMethod().equals(NATIVE))
        {
            List idMethodParams = getIdMethodParameters();
            if (idMethodParams == null)
            {
                result = getName() + "_SEQ";
            }
            else
            {
                result = ((IdMethodParameter) idMethodParams.get(0)).getValue();
            }
        }
        return result;
    }

    /**
     * Returns a List containing all the indices in the table
     *
     * @return A List containing all the indices
     */
    public List getIndices()
    {
        return indices;
    }

    /**
     * Returns a List containing all the UKs in the table
     *
     * @return A List containing all the UKs
     */
    public List getUnices()
    {
        return unices;
    }

    /**
     * Returns a specified column.
     *
     * @param name name of the column
     * @return Return a Column object or null if it does not exist.
     */
    public Column getColumn(String name)
    {
        return (Column) columnsByName.get(name);
    }

    /**
     * Returns a specified column.
     *
     * @param javaName java name of the column
     * @return Return a Column object or null if it does not exist.
     */
    public Column getColumnByJavaName(String javaName)
    {
        return (Column) columnsByJavaName.get(javaName);
    }

    /**
     * Return the first foreign key that includes col in it's list
     * of local columns.  Eg. Foreign key (a,b,c) refrences tbl(x,y,z)
     * will be returned of col is either a,b or c.
     *
     * @param col column name included in the key
     * @return Return a Column object or null if it does not exist.
     */
    public ForeignKey getForeignKey(String col)
    {
        ForeignKey firstFK = null;
        for (Iterator iter = foreignKeys.iterator(); iter.hasNext();)
        {
            ForeignKey key = (ForeignKey) iter.next();
            if (key.getLocalColumns().contains(col))
            {
                if (firstFK == null)
                {
                    firstFK = key;
                }
                else
                {
                    //System.out.println(col+" is in multiple FKs.  This is not"
                    //                   + " being handled properly.");
                    //throw new IllegalStateException("Cannot call method if " +
                    //    "column is referenced multiple times");
                }
            }
        }
        return firstFK;
    }

    /**
     * Returns true if the table contains a specified column
     *
     * @param col the column
     * @return true if the table contains the column
     */
    public boolean containsColumn(Column col)
    {
        return columnList.contains(col);
    }

    /**
     * Returns true if the table contains a specified column
     *
     * @param name name of the column
     * @return true if the table contains the column
     */
    public boolean containsColumn(String name)
    {
        return (getColumn(name) != null);
    }

    /**
     * Set the parent of the table
     *
     * @param parent the parant database
     */
    public void setDatabase(Database parent)
    {
        tableParent = parent;
    }

    /**
     * Get the parent of the table
     *
     * @return the parant database
     */
    public Database getDatabase()
    {
        return tableParent;
    }

    /**
     * Flag to determine if code/sql gets created for this table.
     * Table will be skipped, if return true.
     * @return value of forReferenceOnly.
     */
    public boolean isForReferenceOnly()
    {
        return forReferenceOnly;
    }

    /**
     * Flag to determine if code/sql gets created for this table.
     * Table will be skipped, if set to true.
     * @param v  Value to assign to forReferenceOnly.
     */
    public void setForReferenceOnly(boolean  v)
    {
        this.forReferenceOnly = v;
    }

    /**
     * Returns a XML representation of this table.
     *
     * @return XML representation of this table
     */
    public String toString()
    {
        StringBuffer result = new StringBuffer();

        result.append ("<table name=\"")
              .append(name)
              .append('\"');

        if (javaName != null)
        {
            result.append(" javaName=\"")
                  .append(javaName)
                  .append('\"');
        }

        if (idMethod != null)
        {
            result.append(" idMethod=\"")
                  .append(idMethod)
                  .append('\"');
        }

        if (skipSql)
        {
            result.append(" skipSql=\"")
                  .append(new Boolean(skipSql))
                  .append('\"');
        }

        if (abstractValue)
        {
            result.append(" abstract=\"")
                  .append(new Boolean(abstractValue))
                  .append('\"');
        }

        if (baseClass != null)
        {
            result.append(" baseClass=\"")
                  .append(baseClass)
                  .append('\"');
        }

        if (basePeer != null)
        {
            result.append(" basePeer=\"")
                  .append(basePeer)
                  .append('\"');
        }

        result.append(">\n");

        if (columnList != null)
        {
            for (Iterator iter = columnList.iterator(); iter.hasNext();)
            {
                result.append(iter.next());
            }
        }

        if (foreignKeys != null)
        {
            for (Iterator iter = foreignKeys.iterator(); iter.hasNext();)
            {
                result.append(iter.next());
            }
        }

        if (idMethodParameters != null)
        {
            Iterator iter = idMethodParameters.iterator();
            while (iter.hasNext())
            {
                result.append(iter.next());
            }
        }

        result.append ("</table>\n");

        return result.toString();
    }

    /**
     * Returns the collection of Columns which make up the single primary
     * key for this table.
     *
     * @return A list of the primary key parts.
     */
    public List getPrimaryKey()
    {
        List pk = new ArrayList(columnList.size());

        Iterator iter = columnList.iterator();
        while (iter.hasNext())
        {
            Column col = (Column) iter.next();
            if (col.isPrimaryKey())
            {
                pk.add(col);
            }
        }
        return pk;
    }

    /**
     * Determine whether this table has a primary key.
     *
     * @return Whether this table has any primary key parts.
     */
    public boolean hasPrimaryKey()
    {
        return (getPrimaryKey().size() > 0);
    }

    /**
     * Returns all parts of the primary key, separated by commas.
     *
     * @return A CSV list of primary key parts.
     */
    public String printPrimaryKey()
    {
        return printList(columnList);
    }

    /**
     * Returns the elements of the list, separated by commas.
     *
     * @param list a list of Columns
     * @return A CSV list.
     */
    private String printList(List list)
    {
        StringBuffer result = new StringBuffer();
        boolean comma = false;
        for (Iterator iter = list.iterator(); iter.hasNext();)
        {
            Column col = (Column) iter.next();
            if (col.isPrimaryKey())
            {
                if (comma)
                {
                    result.append(',');
                }
                else
                {
                    comma = true;
                }
                result.append(col.getName());
            }
        }
        return result.toString();
    }

    /**
     * Force all columns to set the correctGetters property.
     *
     * @param correctGetters The new value of the correctGetters property.
     * @since 3.2
     */
    public void setCorrectGetters(Boolean value)
    {
        boolean correctGetters = value != null && value.booleanValue();
        for (Iterator it = columnList.iterator(); it.hasNext(); )
        {
            Column col = (Column) it.next();
            col.setCorrectGetters(correctGetters);
        }
    }

    public int compareTo(Object o) {
        if(this.equals(o))
            return 0;
        return -1;
    }
}
