<?php

/*
 *  $Id: PHP5BasicObjectBuilder.php 566 2007-02-02 16:27:12Z hans $
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * This software consists of voluntary contributions made by many individuals
 * and is licensed under the LGPL. For more information please see
 * <http://propel.phpdb.org>.
 */

require_once 'propel/engine/builder/om/ObjectBuilder.php';

/**
 * Generates a PHP5 base Object class for user object model (OM).
 *
 * This class produces the base object class (e.g. BaseMyTable) which contains all
 * the custom-built accessor and setter methods.
 *
 * This class replaces the Object.tpl, with the intent of being easier for users
 * to customize (through extending & overriding).
 *
 * @author     Hans Lellelid <hans@xmpl.org>
 * @package    propel.engine.builder.om.php5
 */
class PHP5BasicObjectBuilder extends ObjectBuilder {

    /**
     * Gets the package for the [base] object classes.
     * @return     string
     */
    public function getPackage()
    {
        return parent::getPackage() . ".om";
    }

    /**
     * Returns the name of the current class being built.
     * @return     string
     */
    public function getClassname()
    {
        return $this->getBuildProperty('basePrefix') . $this->getStubObjectBuilder()->getClassname();
    }

    /**
     * Adds the include() statements for files that this class depends on or utilizes.
     * @param      string &$script The script will be modified in this method.
     */
    protected function addIncludes(&$script)
    {

        $table = $this->getTable();
        $package = $this->getPackage();
        $parentClass = $this->getBaseClass();
        $interface = $this->getInterface();

        $script .= "
require_once '".$this->getFilePath($parentClass)."';
";



        if (!empty($interface)) {
            $script .= "
require_once '".$this->getFilePath($interface)."';
";
        }


        if (!$table->isAlias()) {

            // If any columns in table are BLOB or CLOB then we need to make
            // sure those classes are included so we can do things like
            // if ($v instanceof Lob) etc.

            $includes_lobs = false;
            foreach ($table->getColumns() as $col) {
                if ($col->isLob()) {
                    $includes_lobs = true;
                    break;
                }
            }

            if($includes_lobs) {
                $script .= "
include_once 'creole/util/Clob.php';
include_once 'creole/util/Blob.php';
";
            }
        } // if table is not alias

        $script .= "

include_once 'propel/util/Criteria.php';
";

        $script .= "
include_once '".$this->getStubPeerBuilder()->getClassFilePath()."';
";
    } // addIncludes()

    /**
     * Adds class phpdoc comment and openning of class.
     * @param      string &$script The script will be modified in this method.
     */
    protected function addClassOpen(&$script)
    {

        $table = $this->getTable();
        $tableName = $table->getName();
        $tableDesc = $table->getDescription();
        $interface = $this->getInterface();

        $script .= "
/**
 * Base class that represents a row from the '$tableName' table.
 *
 * $tableDesc
 *";
        if ($this->getBuildProperty('addTimeStamp')) {
            $now = strftime('%c');
            $script .= "
 * This class was autogenerated by Propel on:
 *
 * $now
 *";
        }
        $script .= "
 * @package    workflow.".$this->getPackage()."
 */
abstract class ".$this->getClassname()." extends ".ClassTools::classname($this->getBaseClass())." ";

        $interface = ClassTools::getInterface($table);
        if ($interface) {
            $script .= "implements " . ClassTools::classname($interface);
        }

        $script .= "\n{\n";
    }

    /**
     * Specifies the methods that are added as part of the basic OM class.
     * This can be overridden by subclasses that wish to add more methods.
     * @see        ObjectBuilder::addClassBody()
     */
    protected function addClassBody(&$script)
    {
        $table = $this->getTable();
        if (!$table->isAlias()) {
            $this->addConstants($script);
            $this->addAttributes($script);
        }

        $this->addColumnAccessorMethods($script);
        $this->addColumnMutatorMethods($script);

        $this->addHydrate($script);

        $this->addManipulationMethods($script);
        $this->addValidationMethods($script);

        if ($this->isAddGenericAccessors()) {
            $this->addGetByName($script);
            $this->addGetByPosition($script);
            $this->addToArray($script);
        }

        if ($this->isAddGenericMutators()) {
            $this->addSetByName($script);
            $this->addSetByPosition($script);
            $this->addFromArray($script);
        }

        $this->addBuildCriteria($script);
        $this->addBuildPkeyCriteria($script);
        $this->addGetPrimaryKey($script);
        $this->addSetPrimaryKey($script);

        $this->addCopy($script);

        if (!$table->isAlias()) {
            $this->addGetPeer($script);
        }
    }

    /**
     * Closes class.
     * @param      string &$script The script will be modified in this method.
     */
    protected function addClassClose(&$script)
    {
        $script .= "}

";
    }

    /**
     * Adds any constants to the class.
     * @param      string &$script The script will be modified in this method.
     */
    protected function addConstants(&$script)
    {
        // nothing to do here any more
        // fieldnameTypeConstants have been moved to class BasePeer [sv]
    }

    /**
     * Adds class attributes.
     * @param      string &$script The script will be modified in this method.
     */
    protected function addAttributes(&$script)
    {
        $script .= "
    /**
     * The Peer class.
     * Instance provides a convenient way of calling static methods on a class
     * that calling code may not be able to identify.
     * @var        ".$this->getPeerClassname()."
    */
    protected static \$peer;
";
        if (!$this->getTable()->isAlias()) {
            $this->addColumnAttributes($script);
        }
    }

    /**
     * Adds variables that store column values.
     * @param      string &$script The script will be modified in this method.
     * @see        addColumnNameConstants()
     */
    protected function addColumnAttributes(&$script) {

        $table = $this->getTable();

        foreach ($table->getColumns() as $col) {

            $cptype = $col->getPhpNative();
            $clo=strtolower($col->getName());
            $defVal = "";
            if (($val = $col->getPhpDefaultValue()) !== null) {
                settype($val, $cptype);
                $defaultValue = var_export($val, true);
                $defVal = " = " . $defaultValue;
            }

            $script .= "
    /**
     * The value for the $clo field.
     * @var        $cptype
     */
    protected \$" . $clo . $defVal . ";
";

            if ($col->isLazyLoad()) {
                $script .= "
    /**
     * Whether the lazy-loaded $clo value has been loaded from database.
     * This is necessary to avoid repeated lookups if $clo column is NULL in the db.
     * @var        boolean
     */
    protected \$".$clo."_isLoaded = false;
";
            }

        }  // foreach col

    } // addColumnAttributes()

    /**
     * Adds the getPeer() method.
     * This is a convenient, non introspective way of getting the Peer class for a particular object.
     * @param      string &$script The script will be modified in this method.
     */
    protected function addGetPeer(&$script)
    {
        $script .= "
    /**
     * Returns a peer instance associated with this om.
     *
     * Since Peer classes are not to have any instance attributes, this method returns the
     * same instance for all member of this class. The method could therefore
     * be static, but this would prevent one from overriding the behavior.
     *
     * @return     ".$this->getPeerClassname()."
     */
    public function getPeer()
    {
        if (self::\$peer === null) {
            self::\$peer = new ".$this->getPeerClassname()."();
        }
        return self::\$peer;
    }
";
    }

    // --------------------------------------------------------------
    //
    // A C C E S S O R    M E T H O D S
    //
    // --------------------------------------------------------------

    /**
     * Adds a date/time/timestamp getter method.
     * @param      string &$script The script will be modified in this method.
     * @param      Column $col The current column.
     * @see        parent::addColumnAccessors()
     */
    protected function addTemporalAccessor(&$script, $col)
    {
        $cfc=$col->getPhpName();
        $clo=strtolower($col->getName());

        // these default values are based on the Creole defaults
        // the date and time default formats are locale-sensitive
        if ($col->getType() === PropelTypes::DATE) {
            $defaultfmt = $this->getBuildProperty('defaultDateFormat');
        } elseif ($col->getType() === PropelTypes::TIME) {
            $defaultfmt = $this->getBuildProperty('defaultTimeFormat');
        } elseif ($col->getType() === PropelTypes::TIMESTAMP) {
            $defaultfmt = $this->getBuildProperty('defaultTimeStampFormat');
        }

        // if the default format property was an empty string, then we'll set it
        // to NULL, which will return the "native" integer timestamp
        if (empty($defaultfmt)) { $defaultfmt = null; }

        $script .= "
    /**
     * Get the [optionally formatted] [$clo] column value.
     * ".$col->getDescription()."
     * @param      string \$format The date/time format string (either date()-style or strftime()-style).
     *                          If format is NULL, then the integer unix timestamp will be returned.
     * @return     mixed Formatted date/time value as string or integer unix timestamp (if format is NULL).
     * @throws     PropelException - if unable to convert the date/time to timestamp.
     */
    public function get$cfc(\$format = ".var_export($defaultfmt, true)."";
        if ($col->isLazyLoad()) $script .= ", \$con = null";
        $script .= ")
    {
";
        if ($col->isLazyLoad()) {
            $script .= "
        if (!\$this->".$clo."_isLoaded && \$this->$clo === null && !\$this->isNew()) {
            \$this->load$cfc(\$con);
        }
";
        }
        $script .= "
        if (\$this->$clo === null || \$this->$clo === '') {
            return null;
        } elseif (!is_int(\$this->$clo)) {
            // a non-timestamp value was set externally, so we convert it
            \$ts = strtotime(\$this->$clo);
            if (\$ts === -1 || \$ts === false) {
                throw new PropelException(\"Unable to parse value of [$clo] as date/time value: \" .
                    var_export(\$this->$clo, true));
            }
        } else {
            \$ts = \$this->$clo;
        }
        if (\$format === null) {
            return \$ts;
        } elseif (strpos(\$format, '%') !== false) {
            return strftime(\$format, \$ts);
        } else {
            return date(\$format, \$ts);
        }
    }
";
    } // addTemporalAccessor

    /**
     * Adds a normal (non-temporal) getter method.
     * @param      string &$script The script will be modified in this method.
     * @param      Column $col The current column.
     * @see        parent::addColumnAccessors()
     */
    protected function addGenericAccessor(&$script, $col)
    {
        $cfc=$col->getPhpName();
        $clo=strtolower($col->getName());

        $script .= "
    /**
     * Get the [$clo] column value.
     * ".$col->getDescription()."
     * @return     ".$col->getPhpNative()."
     */
    public function get$cfc(";
        if ($col->isLazyLoad()) $script .= "\$con = null";
        $script .= ")
    {
";
        if ($col->isLazyLoad()) {
            $script .= "
        if (!\$this->".$clo."_isLoaded && \$this->$clo === null && !\$this->isNew()) {
            \$this->load$cfc(\$con);
        }
";
        }
        $script .= "
        return \$this->$clo;
    }
";
    }

    /**
     * Adds the lazy loader method.
     * @param      string &$script The script will be modified in this method.
     * @param      Column $col The current column.
     * @see        parent::addColumnAccessors()
     */
    protected function addLazyLoader(&$script, $col)
    {
        $cfc=$col->getPhpName();
        $clo=strtolower($col->getName());

        $script .= "
    /**
     * Load the value for the lazy-loaded [$clo] column.
     *
     * This method performs an additional query to return the value for
     * the [$clo] column, since it is not populated by
     * the hydrate() method.
     *
     * @param      \$con Connection
     * @return     void
     * @throws     PropelException - any underlying error will be wrapped and re-thrown.
     */
    protected function load$cfc(\$con = null)
    {
        \$c = \$this->buildPkeyCriteria();
        \$c->addSelectColumn(".$this->getColumnConstant($col).");
        try {
            \$rs = ".$this->getPeerClassname()."::doSelectRS(\$c, \$con);
            \$rs->next();
";
        $affix = CreoleTypes::getAffix(CreoleTypes::getCreoleCode($col->getType()));
        $clo = strtolower($col->getName());
        switch($col->getType()) {
             case PropelTypes::DATE:
             case PropelTypes::TIME:
             case PropelTypes::TIMESTAMP:
                $script .= "
            \$this->$clo = \$rs->get$affix(1, null);
";
                break;
            default:
                $script .= "
            \$this->$clo = \$rs->get$affix(1);
";
        } // switch
        $script .= "
            \$this->".$clo."_isLoaded = true;
        } catch (Exception \$e) {
            throw new PropelException(\"Error loading value for [$clo] column on demand.\", \$e);
        }
    }
";

    } // addLazyLoader()



    // --------------------------------------------------------------
    //
    // M U T A T O R    M E T H O D S
    //
    // --------------------------------------------------------------

    /**
     * Adds the open of the mutator (setter) method for a column.
     * @param      string &$script The script will be modified in this method.
     * @param      Column $col The current column.
     */
    protected function addMutatorOpen(&$script, Column $col)
    {
        $cfc=$col->getPhpName();
        $clo=strtolower($col->getName());

        $script .= "
    /**
     * Set the value of [$clo] column.
     * ".$col->getDescription()."
     * @param      ".$col->getPhpNative()." \$v new value
     * @return     void
     */
    public function set$cfc(\$v)
    {
";
        if ($col->isLazyLoad()) {
            $script .= "
        // explicitly set the is-loaded flag to true for this lazy load col;
        // it doesn't matter if the value is actually set or not (logic below) as
        // any attempt to set the value means that no db lookup should be performed
        // when the get$cfc() method is called.
        \$this->".$clo."_isLoaded = true;
";
        }

    }

    /**
     * Adds the close of the mutator (setter) method for a column.
     * This can be overridden (e.g. by PHP5ComplexObjectBuilder) if additional functionality is needed.
     * @param      string &$script The script will be modified in this method.
     * @param      Column $col The current column.
     */
    protected function addMutatorClose(&$script, Column $col)
    {
                $script .= "
    } // set".$col->getPhpName()."()
";
    }

    /**
     * Adds a setter for date/time/timestamp columns.
     * @param      string &$script The script will be modified in this method.
     * @param      Column $col The current column.
     * @see        parent::addColumnMutators()
     */
    protected function addLobMutator(&$script, Column $col)
    {
        $this->addMutatorOpen($script, $col);
        $clo = strtolower($col->getName());
        // Setting of LOB columns gets some special handling

        if ($col->getPropelType() === PropelTypes::BLOB || $col->getPropelType() === PropelTypes::LONGVARBINARY ) {
            $lobClass = 'Blob';
        } else {
            $lobClass = 'Clob';
        }
        $script .= "
        // if the passed in parameter is the *same* object that
        // is stored internally then we use the Lob->isModified()
        // method to know whether contents changed.
        if (\$v instanceof Lob && \$v === \$this->$clo) {
            \$changed = \$v->isModified();
        } else {
            \$changed = (\$this->$clo !== \$v);
        }
        if (\$changed) {
            if ( !(\$v instanceof Lob) ) {
                \$obj = new $lobClass();
                \$obj->setContents(\$v);
            } else {
                \$obj = \$v;
            }
            \$this->$clo = \$obj;
            \$this->modifiedColumns[] = ".$this->getColumnConstant($col).";
        }
";
        $this->addMutatorClose($script, $col);

    } // addLobMutatorSnippet


    /**
     * Adds a setter method for date/time/timestamp columns.
     * @param      string &$script The script will be modified in this method.
     * @param      Column $col The current column.
     * @see        parent::addColumnMutators()
     */
    protected function addTemporalMutator(&$script, Column $col)
    {
        $clo = strtolower($col->getName());

        $defaultValue = null;
        if (($val = $col->getPhpDefaultValue()) !== null) {
            settype($val, $col->getPhpNative());
            $defaultValue = var_export($val, true);
        }

        $this->addMutatorOpen($script, $col);

        $script .= "
        if (\$v !== null && !is_int(\$v)) {
            \$ts = strtotime(\$v);";
        if ($col->getPhpDefaultValue() != 1) {
            $script .= "
            //Date/time accepts null values
            if (\$v == '') {
                \$ts = null;
            }";
        }
        $script .="
            if (\$ts === -1 || \$ts === false) {
                throw new PropelException(\"Unable to parse date/time value for [$clo] from input: \" .
                    var_export(\$v, true));
            }
        } else {
            \$ts = \$v;
        }
        if (\$this->$clo !== \$ts";
        if ($defaultValue !== null) {
            $script .= " || \$ts === $defaultValue";
        }
        $script .= ") {
            \$this->$clo = \$ts;
            \$this->modifiedColumns[] = ".$this->getColumnConstant($col).";
        }
";
        $this->addMutatorClose($script, $col);
    }

    /**
     * Adds setter method for "normal" columns.
     * @param      string &$script The script will be modified in this method.
     * @param      Column $col The current column.
     * @see        parent::addColumnMutators()
     */
    protected function addDefaultMutator(&$script, Column $col)
    {
        $clo = strtolower($col->getName());

        // FIXME: refactor this
        $defaultValue = null;
        if (($val = $col->getPhpDefaultValue()) !== null) {
            settype($val, $col->getPhpNative());
            $defaultValue = var_export($val, true);
        }

        $this->addMutatorOpen($script, $col);

        // Perform some smart checking here to handle possible type discrepancies
        // between the passed-in value and the value from the DB

        if ($col->getPhpNative() === "int") {
            $script .= "
        // Since the native PHP type for this column is integer,
        // we will cast the input value to an int (if it is not).
        if (\$v !== null && !is_int(\$v) && is_numeric(\$v)) {
            \$v = (int) \$v;
        }
";
        } elseif ($col->getPhpNative() === "string") {
            $script .= "
        // Since the native PHP type for this column is string,
        // we will cast the input to a string (if it is not).
        if (\$v !== null && !is_string(\$v)) {
            \$v = (string) \$v;
        }
";
        }

        $script .= "
        if (\$this->$clo !== \$v";
        if ($defaultValue !== null) {
            $script .= " || \$v === $defaultValue";
        }
        $script .= ") {
            \$this->$clo = \$v;
            \$this->modifiedColumns[] = ".$this->getColumnConstant($col).";
        }
";
        $this->addMutatorClose($script, $col);
    }

    /**
     * Adds the hydrate() method, which sets attributes of the object based on a ResultSet.
     */
    protected function addHydrate(&$script)
    {
        $table = $this->getTable();

        $script .= "
    /**
     * Hydrates (populates) the object variables with values from the database resultset.
     *
     * An offset (1-based \"start column\") is specified so that objects can be hydrated
     * with a subset of the columns in the resultset rows.  This is needed, for example,
     * for results of JOIN queries where the resultset row includes columns from two or
     * more tables.
     *
     * @param      ResultSet \$rs The ResultSet class with cursor advanced to desired record pos.
     * @param      int \$startcol 1-based offset column which indicates which restultset column to start with.
     * @return     int next starting column
     * @throws     PropelException  - Any caught Exception will be rewrapped as a PropelException.
     */
    public function hydrate(ResultSet \$rs, \$startcol = 1)
    {
        try {
";
            $n = 0;
            foreach($table->getColumns() as $col) {
                if(!$col->isLazyLoad()) {
                    $affix = CreoleTypes::getAffix(CreoleTypes::getCreoleCode($col->getType()));
                    $clo = strtolower($col->getName());
                    switch($col->getType()) {

                        case PropelTypes::DATE:
                        case PropelTypes::TIME:
                        case PropelTypes::TIMESTAMP:
                            $script .= "
            \$this->$clo = \$rs->get$affix(\$startcol + $n, null);
";
                            break;
                        default:
                            $script .= "
            \$this->$clo = \$rs->get$affix(\$startcol + $n);
";
                    }
                    $n++;
                } // if col->isLazyLoad()
            } /* foreach */

            if ($this->getBuildProperty("addSaveMethod")) {
                $script .= "
            \$this->resetModified();
";
            }

            $script .= "
            \$this->setNew(false);

            // FIXME - using NUM_COLUMNS may be clearer.
            return \$startcol + $n; // $n = ".$this->getPeerClassname()."::NUM_COLUMNS - ".$this->getPeerClassname()."::NUM_LAZY_LOAD_COLUMNS).

        } catch (Exception \$e) {
            throw new PropelException(\"Error populating ".$table->getPhpName()." object\", \$e);
        }
    }
";

    } // addHydrate()


    /**
     *
     */
    protected function addBuildPkeyCriteria(&$script) {


        $script .= "
    /**
     * Builds a Criteria object containing the primary key for this object.
     *
     * Unlike buildCriteria() this method includes the primary key values regardless
     * of whether or not they have been modified.
     *
     * @return     Criteria The Criteria object containing value(s) for primary key(s).
     */
    public function buildPkeyCriteria()
    {
        \$criteria = new Criteria(".$this->getPeerClassname()."::DATABASE_NAME);
";
        foreach ($this->getTable()->getColumns() as $col) {
            $clo = strtolower($col->getName());
            if ($col->isPrimaryKey()) {
                $script .= "
        \$criteria->add(".$this->getColumnConstant($col).", \$this->$clo);";
            }
        }

        $script .= "

        return \$criteria;
    }
";

    }

    /**
     *
     */
    protected function addBuildCriteria(&$script)
    {
        $script .= "
    /**
     * Build a Criteria object containing the values of all modified columns in this object.
     *
     * @return     Criteria The Criteria object containing all modified values.
     */
    public function buildCriteria()
    {
        \$criteria = new Criteria(".$this->getPeerClassname()."::DATABASE_NAME);
";
        foreach ($this->getTable()->getColumns() as $col) {
            $clo = strtolower($col->getName());
            $script .= "
        if (\$this->isColumnModified(".$this->getColumnConstant($col).")) {
            \$criteria->add(".$this->getColumnConstant($col).", \$this->$clo);
        }
";
        }
        $script .= "

        return \$criteria;
    }
";
    } // addBuildCriteria()

    protected function addToArray(&$script)
    {
        $script .= "
    /**
     * Exports the object as an array.
     *
     * You can specify the key type of the array by passing one of the class
     * type constants.
     *
     * @param      string \$keyType One of the class type constants TYPE_PHPNAME,
     *                        TYPE_COLNAME, TYPE_FIELDNAME, TYPE_NUM
     * @return     an associative array containing the field names (as keys) and field values
     */
    public function toArray(\$keyType = BasePeer::TYPE_PHPNAME)
    {
        \$keys = ".$this->getPeerClassname()."::getFieldNames(\$keyType);
        \$result = array(";
        foreach ($this->getTable()->getColumns() as $num => $col) {
            $script .= "
            \$keys[$num] => \$this->get".$col->getPhpName()."(),";
        }
        $script .= "
        );
        return \$result;
    }
";
    } // addToArray()

    protected function addGetByName(&$script)
    {
        $script .= "
    /**
     * Retrieves a field from the object by name passed in as a string.
     *
     * @param      string \$name name
     * @param      string \$type The type of fieldname the \$name is of:
     *                     one of the class type constants TYPE_PHPNAME,
     *                     TYPE_COLNAME, TYPE_FIELDNAME, TYPE_NUM
     * @return     mixed Value of field.
     */
    public function getByName(\$name, \$type = BasePeer::TYPE_PHPNAME)
    {
        \$pos = ".$this->getPeerClassname()."::translateFieldName(\$name, \$type, BasePeer::TYPE_NUM);
        return \$this->getByPosition(\$pos);
    }
";
    }

    protected function addGetByPosition(&$script)
    {
        $table = $this->getTable();
        $script .= "
    /**
     * Retrieves a field from the object by Position as specified in the xml schema.
     * Zero-based.
     *
     * @param      int \$pos position in xml schema
     * @return     mixed Value of field at \$pos
     */
    public function getByPosition(\$pos)
    {
        switch(\$pos) {";
    $i = 0;
    foreach ($table->getColumns() as $col) {
        $cfc = $col->getPhpName();
        $cptype = $col->getPhpNative();// not safe to use it because some methods may return objects (Blob)
$script .= "
            case $i:
                return \$this->get$cfc();
                break;";
        $i++;
    } /* foreach */
$script .= "
            default:
                return null;
                break;
        } // switch()
    }
";
    }

    protected function addSetByName(&$script)
    {
        $table = $this->getTable();
        $script .= "
    /**
     * Sets a field from the object by name passed in as a string.
     *
     * @param      string \$name peer name
     * @param      mixed \$value field value
     * @param      string \$type The type of fieldname the \$name is of:
     *                     one of the class type constants TYPE_PHPNAME,
     *                     TYPE_COLNAME, TYPE_FIELDNAME, TYPE_NUM
     * @return     void
     */
    public function setByName(\$name, \$value, \$type = BasePeer::TYPE_PHPNAME)
    {
        \$pos = ".$this->getPeerClassname()."::translateFieldName(\$name, \$type, BasePeer::TYPE_NUM);
        return \$this->setByPosition(\$pos, \$value);
    }
";
    }

    protected function addSetByPosition(&$script)
    {
        $table = $this->getTable();
        $script .= "
    /**
     * Sets a field from the object by Position as specified in the xml schema.
     * Zero-based.
     *
     * @param      int \$pos position in xml schema
     * @param      mixed \$value field value
     * @return     void
     */
    public function setByPosition(\$pos, \$value)
    {
        switch(\$pos) {";
        $i = 0;
        foreach ($table->getColumns() as $col) {
            $cfc = $col->getPhpName();
            $cptype = $col->getPhpNative();
            $script .= "
            case $i:
                \$this->set$cfc(\$value);
                break;";
            $i++;
        } /* foreach */
        $script .= "
        } // switch()
    }
";
    } // addSetByPosition()

    protected function addFromArray(&$script)
    {
        $table = $this->getTable();
        $script .= "
    /**
     * Populates the object using an array.
     *
     * This is particularly useful when populating an object from one of the
     * request arrays (e.g. \$_POST).  This method goes through the column
     * names, checking to see whether a matching key exists in populated
     * array. If so the setByName() method is called for that column.
     *
     * You can specify the key type of the array by additionally passing one
     * of the class type constants TYPE_PHPNAME, TYPE_COLNAME, TYPE_FIELDNAME,
     * TYPE_NUM. The default key type is the column's phpname (e.g. 'authorId')
     *
     * @param      array  \$arr     An array to populate the object from.
     * @param      string \$keyType The type of keys the array uses.
     * @return     void
     */
    public function fromArray(\$arr, \$keyType = BasePeer::TYPE_PHPNAME)
    {
        \$keys = ".$this->getPeerClassname()."::getFieldNames(\$keyType);
";
        foreach ($table->getColumns() as $num => $col) {
            $cfc = $col->getPhpName();
            $cptype = $col->getPhpNative();
            $script .= "
        if (array_key_exists(\$keys[$num], \$arr)) {
            \$this->set$cfc(\$arr[\$keys[$num]]);
        }
";
        } /* foreach */
        $script .= "
    }
";
    } // addFromArray



    protected function addDelete(&$script)
    {
        $script .= "
    /**
     * Removes this object from datastore and sets delete attribute.
     *
     * @param      Connection \$con
     * @return     void
     * @throws     PropelException
     * @see        BaseObject::setDeleted()
     * @see        BaseObject::isDeleted()
     */
    public function delete(\$con = null)
    {
        if (\$this->isDeleted()) {
            throw new PropelException(\"This object has already been deleted.\");
        }

        if (\$con === null) {
            \$con = Propel::getConnection(".$this->getPeerClassname()."::DATABASE_NAME);
        }

        try {
            \$con->begin();
            ".$this->getPeerClassname()."::doDelete(\$this, \$con);
            \$this->setDeleted(true);
            \$con->commit();
        } catch (PropelException \$e) {
            \$con->rollback();
            throw \$e;
        }
    }
";
    } // addDelete()

    /**
     * Adds the methods related to saving and deleting the object.
     * @param      string &$script The script will be modified in this method.
     */
    protected function addManipulationMethods(&$script)
    {
        $this->addDelete($script);
        $this->addSave($script);
    }

    /**
     * Adds the methods related to validationg the object.
     * @param      string &$script The script will be modified in this method.
     */
    protected function addValidationMethods(&$script)
    {
        $this->addValidationFailuresAttribute($script);
        $this->addGetValidationFailures($script);
        $this->addValidate($script);
    }

    /**
     * Adds the save() method.
     * @param      string &$script The script will be modified in this method.
     */
    protected function addSave(&$script)
    {
        $table = $this->getTable();
        $script .= "
    /**
     * Stores the object in the database.
     *
     * If the object is new, it inserts it; otherwise an update is performed.
     *
     * @param      Connection \$con
     * @return     int The number of rows affected by this insert/update operation
     * @throws     PropelException
     */
    public function save(\$con=null)
    {
        \$affectedRows = 0; // initialize var to track total num of affected rows

        // If this object has been modified, then save it to the database.
        if (\$this->isModified()) {
            if (\$this->isNew()) {
                \$pk = ".$this->getPeerClassname()."::doInsert(\$this, \$con);
                \$affectedRows += 1; // we are assuming that there is only 1 row per doInsert() which
                                     // should always be true here (even though technically
                                     // BasePeer::doInsert() can insert multiple rows).
";
        if ($table->getIdMethod() != "none") {
            if (count($pks = $table->getPrimaryKey())) {
                foreach ($pks as $pk) {
                    if ($pk->isAutoIncrement()) {
                        $script .= "
                    \$this->set".$pk->getPhpName()."(\$pk);  //[IMV] update autoincrement primary key
";
                    }
                }
            }
        }
        $script .= "
                    \$this->setNew(false);
            } else {
                    \$affectedRows += ".$this->getPeerClassname()."::doUpdate(\$this, \$con);
            }
                \$this->resetModified(); // [HL] After being saved an object is no longer 'modified'
        } // if \$this->isModified()

        return \$affectedRows;
    } // save()
";

    } // addSave()

    /**
     * Adds the $validationFailures attribute to store ValidationFailed objects.
     * @param      string &$script The script will be modified in this method.
     */
    protected function addValidationFailuresAttribute(&$script)
    {
        $script .= "
    /**
     * Array of ValidationFailed objects.
     * @var        array ValidationFailed[]
     */
    protected \$validationFailures = array();
";
    }

    /**
     * Adds the validate() method.
     * @param      string &$script The script will be modified in this method.
     */
    protected function addValidate(&$script)
    {
        $script .= "
    /**
     * Validates the objects modified field values.
     *
     * If \$columns is either a column name or an array of column names
     * only those columns are validated.
     *
     * @param      mixed \$columns Column name or an array of column names.
     *
     * @return     mixed <code>true</code> if all columns pass validation
     *            or an array of <code>ValidationFailed</code> objects for columns that fail.
     */
    public function validate(\$columns = null)
    {
        if (\$columns) {
            return ".$this->getPeerClassname()."::doValidate(\$this, \$columns);
        }
        return ".$this->getPeerClassname()."::doValidate(\$this);
    }
";

    } // addValidate()

    /**
     * Adds the getValidationFailures() method.
     * @param      string &$script The script will be modified in this method.
     */
    protected function addGetValidationFailures(&$script)
    {
        $script .= "
    /**
     * Gets any ValidationFailed objects that resulted from last call to validate().
     *
     *
     * @return     array ValidationFailed[]
     * @see        validate()
     */
    public function getValidationFailures()
    {
        return \$this->validationFailures;
    }
";
    } // addGetValidationFailures()

    /**
     * Adds the correct getPrimaryKey() method for this object.
     * @param      string &$script The script will be modified in this method.
     */
    protected function addGetPrimaryKey(&$script)
    {
        $pkeys = $this->getTable()->getPrimaryKey();
        if (count($pkeys) == 1) {
            $this->addGetPrimaryKey_SinglePK($script);
        } elseif (count($pkeys) > 1) {
            $this->addGetPrimaryKey_MultiPK($script);
        } else {
            // no primary key -- this is deprecated, since we don't *need* this method anymore
            $this->addGetPrimaryKey_NoPK($script);
        }
    }

    /**
     * Adds the getPrimaryKey() method for tables that contain a single-column primary key.
     * @param      string &$script The script will be modified in this method.
     */
    protected function addGetPrimaryKey_SinglePK(&$script)
    {
        $table = $this->getTable();
        $pkeys = $table->getPrimaryKey();
        $cptype = $pkeys[0]->getPhpType();

        $script .= "
    /**
     * Returns the primary key for this object (row).
     * @return     $cptype
     */
    public function getPrimaryKey()
    {
        return \$this->get".$pkeys[0]->getPhpName()."();
    }
";
    } // addetPrimaryKey_SingleFK

    /**
     * Adds the setPrimaryKey() method for tables that contain a multi-column primary key.
     * @param      string &$script The script will be modified in this method.
     */
    protected function addGetPrimaryKey_MultiPK(&$script)
    {

        $script .= "
    /**
     * Returns the composite primary key for this object.
     * The array elements will be in same order as specified in XML.
     * @return     array
     */
    public function getPrimaryKey()
    {
        \$pks = array();
";
        $i = 0;
        foreach ($this->getTable()->getPrimaryKey() as $pk) {
            $script .= "
        \$pks[$i] = \$this->get".$pk->getPhpName()."();
";
            $i++;
        } /* foreach */
        $script .= "
        return \$pks;
    }
";
    } // addGetPrimaryKey_MultiFK()

    /**
     * Adds the getPrimaryKey() method for objects that have no primary key.
     * This "feature" is dreprecated, since the getPrimaryKey() method is not required
     * by the Persistent interface (or used by the templates).  Hence, this method is also
     * deprecated.
     * @param      string &$script The script will be modified in this method.
     * @deprecated
     */
    protected function addGetPrimaryKey_NoPK(&$script)
    {
        $script .= "
    /**
     * Returns NULL since this table doesn't have a primary key.
     * This method exists only for BC and is deprecated!
     * @return     null
     */
    public function getPrimaryKey()
    {
        return null;
    }
";
    }
    /**
     * Adds the correct setPrimaryKey() method for this object.
     * @param      string &$script The script will be modified in this method.
     */
    protected function addSetPrimaryKey(&$script)
    {
        $pkeys = $this->getTable()->getPrimaryKey();
        if (count($pkeys) == 1) {
            $this->addSetPrimaryKey_SinglePK($script);
        } elseif (count($pkeys) > 1) {
            $this->addSetPrimaryKey_MultiPK($script);
        } else {
            // no primary key -- this is deprecated, since we don't *need* this method anymore
            $this->addSetPrimaryKey_NoPK($script);
        }
    }

    /**
     * Adds the setPrimaryKey() method for tables that contain a single-column primary key.
     * @param      string &$script The script will be modified in this method.
     */
    protected function addSetPrimaryKey_SinglePK(&$script)
    {

        $pkeys = $this->getTable()->getPrimaryKey();
        $col = $pkeys[0];
        $clo=strtolower($col->getName());
        $ctype = $col->getPhpNative();

        $script .= "
    /**
     * Generic method to set the primary key ($clo column).
     *
     * @param      $ctype \$key Primary key.
     * @return     void
     */
    public function setPrimaryKey(\$key)
    {
        \$this->set".$col->getPhpName()."(\$key);
    }
";
    } // addSetPrimaryKey_SinglePK

    /**
     * Adds the setPrimaryKey() method for tables that contain a multi-columnprimary key.
     * @param      string &$script The script will be modified in this method.
     */
    protected function addSetPrimaryKey_MultiPK(&$script)
    {

        $script .="
    /**
     * Set the [composite] primary key.
     *
     * @param      array \$keys The elements of the composite key (order must match the order in XML file).
     * @return     void
     */
    public function setPrimaryKey(\$keys)
    {
";
            $i = 0;
            foreach ($this->getTable()->getPrimaryKey() as $pk) {
                $pktype = $pk->getPhpNative();
                $script .= "
        \$this->set".$pk->getPhpName()."(\$keys[$i]);
";
                $i++;
            } /* foreach ($table->getPrimaryKey() */
            $script .= "
    }
";
    } // addSetPrimaryKey_MultiPK

    /**
     * Adds the setPrimaryKey() method for objects that have no primary key.
     * This "feature" is dreprecated, since the setPrimaryKey() method is not required
     * by the Persistent interface (or used by the templates).  Hence, this method is also
     * deprecated.
     * @param      string &$script The script will be modified in this method.
     * @deprecated
     */
    protected function addSetPrimaryKey_NoPK(&$script)
    {
        $script .="
    /**
     * Dummy primary key setter.
     *
     * This function only exists to preserve backwards compatibility.  It is no longer
     * needed or required by the Persistent interface.  It will be removed in next BC-breaking
     * release of Propel.
     *
     * @deprecated
     */
     public function setPrimaryKey(\$pk)
     {
         // do nothing, because this object doesn't have any primary keys
     }
";
    }

    /**
     * Adds the copy() method, which (in complex OM) includes the $deepCopy param for making copies of related objects.
     * @param      string &$script The script will be modified in this method.
     */
    protected function addCopy(&$script)
    {
        $this->addCopyInto($script);

        $table = $this->getTable();

        $script .= "
    /**
     * Makes a copy of this object that will be inserted as a new row in table when saved.
     * It creates a new object filling in the simple attributes, but skipping any primary
     * keys that are defined for the table.
     *
     * @return     ".$table->getPhpName()." Clone of current object.
     * @throws     PropelException
     */
    public function copy()
    {
        // we use get_class(), because this might be a subclass
        \$clazz = get_class(\$this);
        \$copyObj = new \$clazz();
        \$this->copyInto(\$copyObj);
        return \$copyObj;
    }
";
    } // addCopy()

    /**
     * Adds the copy() method.
     */
    protected function addCopyInto(&$script)
    {
        $table = $this->getTable();

        $script .= "
    /**
     * Sets contents of passed object to values from current object.
     *
     * @param      object \$copyObj An object of ".$table->getPhpName()." (or compatible) type.
     * @return     ".$table->getPhpName()." Clone of current object.
     * @throws     PropelException
     */
    public function copyInto(\$copyObj)
    {
";

        $pkcols = array();
        foreach ($table->getColumns() as $pkcol) {
            if ($pkcol->isPrimaryKey()) {
                $pkcols[] = $pkcol->getName();
            }
        }

        foreach ($table->getColumns() as $col) {
            if (!in_array($col->getName(), $pkcols)) {
                $script .= "
        \$copyObj->set<?php echo $col->getPhpName()?>(\$this-><?php echo strtolower($col->getName()) ?>);";
            }
        } // foreach

        $script .= "

        \$copyObj->setNew(true);";

        foreach ($table->getColumns() as $col) {
            if ($col->isPrimaryKey()) {
                $coldefval = $col->getPhpDefaultValue();
                $coldefval = var_export($coldefval, true);
                $script .= "
        \$copyObj->set".$col->getPhpName() ."($coldefval); // this is a pkey column, so set to default value";
            } // if col->isPrimaryKey
        } // foreach
        $script .= "
        return \$copyObj;
    }
";
    } // addCopy()

} // PHP5BasicObjectBuilder
