<?php

/*
 *  $Id: PHP5BasicPeerBuilder.php 592 2007-03-02 21:41:42Z 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/PeerBuilder.php';

/**
 * Generates a PHP5 base Peer class for user object model (OM).
 *
 * This class produces the base peer class (e.g. BaseMyPeer) which contains all
 * the custom-built query and manipulator methods.
 *
 * This class replaces the Peer.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 PHP5BasicPeerBuilder extends PeerBuilder {

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

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

	/**
	 * 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();

		$basePeerFile = $this->getFilePath($this->basePeerClass);
		$objectFile = $this->getStubObjectBuilder()->getClassFilePath();

		$script .= "
require_once '$basePeerFile';
// The object class -- needed for instanceof checks in this class.
// actual class may be a subclass -- as returned by ".$this->getPeerClassname()."::getOMClass()
include_once '$objectFile';";

		$script .= "
";

	} // addIncludes()

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

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

		$script .= "
/**
 * Base static class for performing query and update operations on the '$tableName' table.
 *
 * $tableDesc
 *";
		if ($this->getBuildProperty('addTimeStamp')) {
			$now = strftime('%c');
			$script .= "
 * This class was autogenerated by Propel on:
 *
 * $now
 *";
		}
		$script .= "
 * @package    ".$this->getPackage()."
 */
abstract class ".$this->getClassname()." {
";
	}

	/**
	 * Closes class.
	 * Adds closing brace at end of class and the static map builder registration code.
	 * @param      string &$script The script will be modified in this method.
	 * @see        addStaticMapBuilderRegistration()
	 */
	protected function addClassClose(&$script)
	{
		$script .= "
} // " . $this->getClassname() . "
";
		$this->addStaticMapBuilderRegistration($script);
	}

	/**
	 * Adds the static map builder registraction code.
	 * @param      string &$script The script will be modified in this method.
	 */
	protected function addStaticMapBuilderRegistration(&$script)
	{
		$table = $this->getTable();
		$mapBuilderFile = $this->getMapBuilderBuilder()->getClassFilePath();

		$script .= "
// static code to register the map builder for this Peer with the main Propel class
if (Propel::isInit()) {
	// the MapBuilder classes register themselves with Propel during initialization
	// so we need to load them here.
	try {
		".$this->getClassname()."::getMapBuilder();
	} catch (Exception \$e) {
		Propel::log('Could not initialize Peer: ' . \$e->getMessage(), Propel::LOG_ERR);
	}
} else {
	// even if Propel is not yet initialized, the map builder class can be registered
	// now and then it will be loaded when Propel initializes.
	require_once '$mapBuilderFile';
	Propel::registerMapBuilder('".$this->getMapBuilderBuilder()->getClasspath()."');
}
";
	}

	/**
	 * Adds constant and variable declarations that go at the top of the class.
	 * @param      string &$script The script will be modified in this method.
	 * @see        addColumnNameConstants()
	 */
	protected function addConstantsAndAttributes(&$script)
	{
		$tableName = $this->getTable()->getName();
		$dbName = $this->getDatabase()->getName();
		$script .= "
	/** the default database name for this class */
	const DATABASE_NAME = '$dbName';

	/** the table name for this class */
	const TABLE_NAME = '$tableName';

	/** A class that can be returned by this peer. */
	const CLASS_DEFAULT = '".$this->getStubObjectBuilder()->getClasspath()."';

	/** The total number of columns. */
	const NUM_COLUMNS = ".$this->getTable()->getNumColumns().";

	/** The number of lazy-loaded columns. */
	const NUM_LAZY_LOAD_COLUMNS = ".$this->getTable()->getNumLazyLoadColumns().";

";
		$this->addColumnNameConstants($script);
		$this->addInheritanceColumnConstants($script);

		$script .= "
	/** The PHP to DB Name Mapping */
	private static \$phpNameMap = null;

";

		$this->addFieldNamesAttribute($script);
		$this->addFieldKeysAttribute($script);

	}

	/**
	 * Adds the COLUMN_NAME contants to the class definition.
	 * @param      string &$script The script will be modified in this method.
	 */
	protected function addColumnNameConstants(&$script)
	{
		foreach ($this->getTable()->getColumns() as $col) {

			$script .= "
	/** the column name for the ".strtoupper($col->getName()) ." field */
	const ".$this->getColumnName($col) ." = '".$this->getTable()->getName().".".strtoupper($col->getName())."';
";
		} // foreach
	}

	protected function addFieldNamesAttribute(&$script)
	{
		$table = $this->getTable();

		$tableColumns = $table->getColumns();
		$tablePhpname = $table->getPhpName();

		$script .= "
	/**
	 * holds an array of fieldnames
	 *
	 * first dimension keys are the type constants
	 * e.g. self::\$fieldNames[self::TYPE_PHPNAME][0] = 'Id'
	 */
	private static \$fieldNames = array (
		BasePeer::TYPE_PHPNAME => array (";
		foreach ($tableColumns as $col) {
			$script .= "'".$col->getPhpName()."', ";
		}
		$script .= "),
		BasePeer::TYPE_COLNAME => array (";
		foreach ($tableColumns as $col) {
			$script .= $this->getColumnConstant($col).", ";
		}
		$script .= "),
		BasePeer::TYPE_FIELDNAME => array (";
		foreach ($tableColumns as $col) {
			$script .= "'".$col->getName()."', ";
		}
		$script .= "),
		BasePeer::TYPE_NUM => array (";
		foreach ($tableColumns as $num => $col) {
			$script .= "$num, ";
		}
		$script .= ")
	);
";
	}

	protected function addFieldKeysAttribute(&$script)
	{
		$table = $this->getTable();

		$tableColumns = $table->getColumns();
		$tablePhpname = $table->getPhpName();

		$script .= "
	/**
	 * holds an array of keys for quick access to the fieldnames array
	 *
	 * first dimension keys are the type constants
	 * e.g. self::\$fieldNames[BasePeer::TYPE_PHPNAME]['Id'] = 0
	 */
	private static \$fieldKeys = array (
		BasePeer::TYPE_PHPNAME => array (";
		foreach ($tableColumns as $num => $col) {
			$script .= "'".$col->getPhpName()."' => $num, ";
		}
		$script .= "),
		BasePeer::TYPE_COLNAME => array (";
		foreach ($tableColumns as $num => $col) {
			$script .= $this->getColumnConstant($col)." => $num, ";
		}
		$script .= "),
		BasePeer::TYPE_FIELDNAME => array (";
		foreach ($tableColumns as $num => $col) {
			$script .= "'".$col->getName()."' => $num, ";
		}
		$script .= "),
		BasePeer::TYPE_NUM => array (";
		foreach ($tableColumns as $num => $col) {
			$script .= "$num, ";
		}
		$script .= ")
	);
";
	} // addFielKeysAttribute


	protected function addGetFieldNames(&$script)
	{
		$script .= "
	/**
	 * Returns an array of of field names.
	 *
	 * @param      string \$type The type of fieldnames to return:
	 *                      One of the class type constants TYPE_PHPNAME,
	 *                      TYPE_COLNAME, TYPE_FIELDNAME, TYPE_NUM
	 * @return     array A list of field names
	 */

	static public function getFieldNames(\$type = BasePeer::TYPE_PHPNAME)
	{
		if (!array_key_exists(\$type, self::\$fieldNames)) {
			throw new PropelException('Method getFieldNames() expects the parameter \$type to be one of the class constants TYPE_PHPNAME, TYPE_COLNAME, TYPE_FIELDNAME, TYPE_NUM. ' . \$type . ' was given.');
		}
		return self::\$fieldNames[\$type];
	}
";

	} // addGetFieldNames()

	protected function addTranslateFieldName(&$script)
	{
		$script .= "
	/**
	 * Translates a fieldname to another type
	 *
	 * @param      string \$name field name
	 * @param      string \$fromType One of the class type constants TYPE_PHPNAME,
	 *                         TYPE_COLNAME, TYPE_FIELDNAME, TYPE_NUM
	 * @param      string \$toType   One of the class type constants
	 * @return     string translated name of the field.
	 */
	static public function translateFieldName(\$name, \$fromType, \$toType)
	{
		\$toNames = self::getFieldNames(\$toType);
		\$key = isset(self::\$fieldKeys[\$fromType][\$name]) ? self::\$fieldKeys[\$fromType][\$name] : null;
		if (\$key === null) {
			throw new PropelException(\"'\$name' could not be found in the field names of type '\$fromType'. These are: \" . print_r(self::\$fieldKeys[\$fromType], true));
		}
		return \$toNames[\$key];
	}
";
	} // addTranslateFieldName()

	/**
	 * Adds the getMapBuilder() method.
	 * @param      string &$script The script will be modified in this method.
	 */
	protected function addGetMapBuilder(&$script)
	{
		$script .= "
	/**
	 * @return     MapBuilder the map builder for this peer
	 * @throws     PropelException Any exceptions caught during processing will be
	 *		 rethrown wrapped into a PropelException.
	 */
	public static function getMapBuilder()
	{
		include_once '" . $this->getMapBuilderBuilder()->getClassFilePath()."';
		return ".$this->basePeerClassname."::getMapBuilder('". $this->getMapBuilderBuilder()->getClasspath() ."');
	}";
	}

	/**
	 * Adds the getPhpNameMap() method.
	 * @param      string &$script The script will be modified in this method.
	 * @todo       Replace with static version (this can be built at build-time).
	 */
	protected function addGetPhpNameMap(&$script)
	{
		$script .= "
	/**
	 * Gets a map (hash) of PHP names to DB column names.
	 *
	 * @return     array The PHP to DB name map for this peer
	 * @throws     PropelException Any exceptions caught during processing will be
	 *		 rethrown wrapped into a PropelException.
	 * @deprecated Use the getFieldNames() and translateFieldName() methods instead of this.
	 */
	public static function getPhpNameMap()
	{
		if (self::\$phpNameMap === null) {
			\$map = ".$this->getTable()->getPhpName()."Peer::getTableMap();
			\$columns = \$map->getColumns();
			\$nameMap = array();
			foreach (\$columns as \$column) {
				\$nameMap[\$column->getPhpName()] = \$column->getColumnName();
			}
			self::\$phpNameMap = \$nameMap;
		}
		return self::\$phpNameMap;
	}";
	}

	/**
	 * Adds the CLASSKEY_* and CLASSNAME_* constants used for inheritance.
	 * @param      string &$script The script will be modified in this method.
	 */
	public function addInheritanceColumnConstants(&$script)
	{
		if ($this->getTable()->getChildrenColumn()) {

			$col = $this->getTable()->getChildrenColumn();
			$tfc = $this->getTable()->getPhpName();
			$cfc = $col->getPhpName();

			if ($col->isEnumeratedClasses()) {

				if ($col->isPrimitiveNumeric()) $quote = "";
				else $quote = '"';

				foreach ($col->getChildren() as $child) {
					$childBuilder = $this->getMultiExtendObjectBuilder();
					$childBuilder->setChild($child);

					$script .= "
	/** A key representing a particular subclass */
	const CLASSKEY_".strtoupper($child->getKey())." = '" . $child->getKey() . "';
";
					
	if (strtoupper($child->getClassname()) != strtoupper($child->getKey())) {
		$script .= "
	/** A key representing a particular subclass */
	const CLASSKEY_".strtoupper($child->getClassname())." = '" . $child->getKey() . "';
";
	}
	
	$script .= "
	/** A class that can be returned by this peer. */
	const CLASSNAME_".strtoupper($child->getKey())." = '". $childBuilder->getClasspath() . "';
";
					} /* foreach children */
			} /* if col->isenumerated...() */
		} /* if table->getchildrencolumn() */

	} //



	/**
	 * Adds the alias() utility method.
	 * @param      string &$script The script will be modified in this method.
	 */
	protected function addAlias(&$script)
	{
		$script .= "
	/**
	 * Convenience method which changes table.column to alias.column.
	 *
	 * Using this method you can maintain SQL abstraction while using column aliases.
	 * <code>
	 *		\$c->addAlias(\"alias1\", TablePeer::TABLE_NAME);
	 *		\$c->addJoin(TablePeer::alias(\"alias1\", TablePeer::PRIMARY_KEY_COLUMN), TablePeer::PRIMARY_KEY_COLUMN);
	 * </code>
	 * @param      string \$alias The alias for the current table.
	 * @param      string \$column The column name for current table. (i.e. ".$this->getTable()->getPhpName()."Peer::COLUMN_NAME).
	 * @return     string
	 */
	public static function alias(\$alias, \$column)
	{
		return str_replace(".$this->getPeerClassname()."::TABLE_NAME.'.', \$alias.'.', \$column);
	}
";
	} // addAliasMethod

	/**
	 * Adds the addSelectColumns() method.
	 * @param      string &$script The script will be modified in this method.
	 */
	protected function addAddSelectColumns(&$script)
	{
		$script .= "
	/**
	 * Add all the columns needed to create a new object.
	 *
	 * Note: any columns that were marked with lazyLoad=\"true\" in the
	 * XML schema will not be added to the select list and only loaded
	 * on demand.
	 *
	 * @param      criteria object containing the columns to add.
	 * @throws     PropelException Any exceptions caught during processing will be
	 *		 rethrown wrapped into a PropelException.
	 */
	public static function addSelectColumns(Criteria \$criteria)
	{
";
		foreach ($this->getTable()->getColumns() as $col) {
			if (!$col->isLazyLoad()) {
				$script .= "
		\$criteria->addSelectColumn(".$this->getPeerClassname()."::".$this->getColumnName($col).");
";
			} // if !col->isLazyLoad
		} // foreach
		$script .="
	}
";
	} // addAddSelectColumns()



	/**
	 * Adds the COUNT constants.
	 * @param      string &$script The script will be modified in this method.
	 */
	protected function addCountConstants(&$script)
	{
		$table = $this->getTable();
		$count_col = "*";
		/*
		* FIXME
		* (HL) wanted to remove this because AFAIK count(*) is generally
		* optimized in databases, and furthermore the code below isn't correct
		* (multi-pkey needs to be accounted for)....
		*/
		if ($table->hasPrimaryKey()) {
			$pk = $table->getPrimaryKey();
			$count_col = $table->getName().".".strtoupper($pk[0]->getName());
		}

		$script .= "
	const COUNT = 'COUNT($count_col)';
	const COUNT_DISTINCT = 'COUNT(DISTINCT $count_col)';
";
	}

	/**
	 * Adds the doCount() method.
	 * @param      string &$script The script will be modified in this method.
	 */
	protected function addDoCount(&$script)
	{
		$script .= "
	/**
	 * Returns the number of rows matching criteria.
	 *
	 * @param      Criteria \$criteria
	 * @param      boolean \$distinct Whether to select only distinct columns (You can also set DISTINCT modifier in Criteria).
	 * @param      Connection \$con
	 * @return     int Number of matching rows.
	 */
	public static function doCount(Criteria \$criteria, \$distinct = false, \$con = null)
	{
		// we're going to modify criteria, so copy it first
		\$criteria = clone \$criteria;

		// clear out anything that might confuse the ORDER BY clause
		\$criteria->clearSelectColumns()->clearOrderByColumns();
		if (\$distinct || in_array(Criteria::DISTINCT, \$criteria->getSelectModifiers())) {
			\$criteria->addSelectColumn(".$this->getPeerClassname()."::COUNT_DISTINCT);
		} else {
			\$criteria->addSelectColumn(".$this->getPeerClassname()."::COUNT);
		}

		// just in case we're grouping: add those columns to the select statement
		foreach(\$criteria->getGroupByColumns() as \$column)
		{
			\$criteria->addSelectColumn(\$column);
		}

		\$rs = ".$this->getPeerClassname()."::doSelectRS(\$criteria, \$con);
		if (\$rs->next()) {
			return \$rs->getInt(1);
		} else {
			// no rows returned; we infer that means 0 matches.
			return 0;
		}
	}";
	}

	/**
	 * Adds the doSelectOne() method.
	 * @param      string &$script The script will be modified in this method.
	 */
	protected function addDoSelectOne(&$script)
	{
		$script .= "
	/**
	 * Method to select one object from the DB.
	 *
	 * @param      Criteria \$criteria object used to create the SELECT statement.
	 * @param      Connection \$con
	 * @return     ".$this->getTable()->getPhpName()."
	 * @throws     PropelException Any exceptions caught during processing will be
	 *		 rethrown wrapped into a PropelException.
	 */
	public static function doSelectOne(Criteria \$criteria, \$con = null)
	{
		\$critcopy = clone \$criteria;
		\$critcopy->setLimit(1);
		\$objects = ".$this->getPeerClassname()."::doSelect(\$critcopy, \$con);
		if (\$objects) {
			return \$objects[0];
		}
		return null;
	}";
	}

	/**
	 * Adds the doSelect() method.
	 * @param      string &$script The script will be modified in this method.
	 */
	protected function addDoSelect(&$script)
	{
		$script .= "
	/**
	 * Method to do selects.
	 *
	 * @param      Criteria \$criteria The Criteria object used to build the SELECT statement.
	 * @param      Connection \$con
	 * @return     array Array of selected Objects
	 * @throws     PropelException Any exceptions caught during processing will be
	 *		 rethrown wrapped into a PropelException.
	 */
	public static function doSelect(Criteria \$criteria, \$con = null)
	{
		return ".$this->getPeerClassname()."::populateObjects(".$this->getPeerClassname()."::doSelectRS(\$criteria, \$con));
	}";
	}

	/**
	 * Adds the doSelectRS() method.
	 * @param      string &$script The script will be modified in this method.
	 */
	protected function addDoSelectRS(&$script)
	{

		$script .= "
	/**
	 * Prepares the Criteria object and uses the parent doSelect()
	 * method to get a ResultSet.
	 *
	 * Use this method directly if you want to just get the resultset
	 * (instead of an array of objects).
	 *
	 * @param      Criteria \$criteria The Criteria object used to build the SELECT statement.
	 * @param      Connection \$con the connection to use
	 * @throws     PropelException Any exceptions caught during processing will be
	 *		 rethrown wrapped into a PropelException.
	 * @return     ResultSet The resultset object with numerically-indexed fields.
	 * @see        ".$this->basePeerClassname."::doSelect()
	 */
	public static function doSelectRS(Criteria \$criteria, \$con = null)
	{
		if (\$con === null) {
			\$con = Propel::getConnection(self::DATABASE_NAME);
		}

		if (!\$criteria->getSelectColumns()) {
			\$criteria = clone \$criteria;
			".$this->getPeerClassname()."::addSelectColumns(\$criteria);
		}

		// Set the correct dbName
		\$criteria->setDbName(self::DATABASE_NAME);

		// BasePeer returns a Creole ResultSet, set to return
		// rows indexed numerically.
		return ".$this->basePeerClassname."::doSelect(\$criteria, \$con);
	}";
	}

	/**
	 * Adds the populateObjects() method.
	 * @param      string &$script The script will be modified in this method.
	 */
	protected function addPopulateObjects(&$script)
	{
		$table = $this->getTable();
		$script .= "
	/**
	 * The returned array will contain objects of the default type or
	 * objects that inherit from the default.
	 *
	 * @throws     PropelException Any exceptions caught during processing will be
	 *		 rethrown wrapped into a PropelException.
	 */
	public static function populateObjects(ResultSet \$rs)
	{
		\$results = array();
	";
		if (!$table->getChildrenColumn()) {
			$script .= "
		// set the class once to avoid overhead in the loop
		\$cls = ".$this->getPeerClassname()."::getOMClass();
		\$cls = Propel::import(\$cls);";
		}

		$script .= "
		// populate the object(s)
		while(\$rs->next()) {
		";
		if ($table->getChildrenColumn()) {
			$script .= "
			// class must be set each time from the record row
			\$cls = Propel::import(".$this->getPeerClassname()."::getOMClass(\$rs, 1));
			\$obj = new \$cls();
			\$obj->hydrate(\$rs);
			\$results[] = \$obj;
			";
		} else {
			$script .= "
			\$obj = new \$cls();
			\$obj->hydrate(\$rs);
			\$results[] = \$obj;
			";
		}
		$script .= "
		}
		return \$results;
	}";
	}

	/**
	 * Adds a getOMClass() for non-abstract tables that have inheritance.
	 * @param      string &$script The script will be modified in this method.
	 */
	protected function addGetOMClass_Inheritance(&$script)
	{
		$col = $this->getTable()->getChildrenColumn();
		$script .= "
	/**
	 * The returned Class will contain objects of the default type or
	 * objects that inherit from the default.
	 *
	 * @param      ResultSet \$rs ResultSet with pointer to record containing om class.
	 * @param      int \$colnum Column to examine for OM class information (first is 1).
	 * @throws     PropelException Any exceptions caught during processing will be
	 *		 rethrown wrapped into a PropelException.
	 */
	public static function getOMClass(ResultSet \$rs, \$colnum)
	{
		try {
";
		if ($col->isEnumeratedClasses()) {
			$script .= "
			\$omClass = null;
			\$classKey = \$rs->getString(\$colnum - 1 + " . $col->getPosition() . ");

			switch(\$classKey) {
";
			foreach ($col->getChildren() as $child) {
				$script .= "
				case self::CLASSKEY_".strtoupper($child->getKey()).":
					\$omClass = self::CLASSNAME_".strtoupper($child->getKey()).";
					break;
";
			} /* foreach */
			$script .= "
				default:
					\$omClass = self::CLASS_DEFAULT;
";
			$script .= "
			} // switch
";
		} else { /* if not enumerated */
			$script .= "
			\$omClass = Propel::import(\$rs->getString(\$colnum - 1 + ".$col->getPosition()."));
";
		}
		$script .= "
		} catch (Exception \$e) {
			throw new PropelException('Unable to get OM class.', \$e);
		}
		return \$omClass;
	}
";
	}

	/**
	 * Adds a getOMClass() signature for abstract tables that have inheritance.
	 * @param      string &$script The script will be modified in this method.
	 */
	protected function addGetOMClass_Inheritance_Abstract(&$script)
	{
		$script .= "
	/**
	 * The returned Class will contain objects of the default type or
	 * objects that inherit from the default.
	 *
	 * This method must be overridden by the stub subclass, because
	 * ".$this->getTable()->getPhpName()." is declared abstract in the schema.
	 *
	 * @param      ResultSet \$rs ResultSet with pointer to record containing om class.
	 * @param      int \$colnum Column to examine for OM class information (first is 1).
	 * @throws     PropelException Any exceptions caught during processing will be
	 *		 rethrown wrapped into a PropelException.
	 */
	abstract public static function getOMClass();
";
	}

	/**
	 * Adds a getOMClass() for non-abstract tables that do note use inheritance.
	 * @param      string &$script The script will be modified in this method.
	 */
	protected function addGetOMClass_NoInheritance(&$script)
	{
		$script .= "
	/**
	 * The class that the Peer will make instances of.
	 *
	 * This uses a dot-path notation which is tranalted into a path
	 * relative to a location on the PHP include_path.
	 * (e.g. path.to.MyClass -> 'path/to/MyClass.php')
	 *
	 * @return     string path.to.ClassName
	 */
	public static function getOMClass()
	{
		return ".$this->getPeerClassname()."::CLASS_DEFAULT;
	}
";
	}

	/**
	 * Adds a getOMClass() signature for abstract tables that do not have inheritance.
	 * @param      string &$script The script will be modified in this method.
	 */
	protected function addGetOMClass_NoInheritance_Abstract(&$script)
	{
		$script .= "
	/**
	 * The class that the Peer will make instances of.
	 *
	 * This method must be overridden by the stub subclass, because
	 * ".$this->getTable()->getPhpName()." is declared abstract in the schema.
	 */
	abstract public static function getOMClass();
";
	}

	/**
	 * Adds the doInsert() method.
	 * @param      string &$script The script will be modified in this method.
	 */
	protected function addDoInsert(&$script)
	{
		$table = $this->getTable();
		$script .= "
	/**
	 * Method perform an INSERT on the database, given a ".$table->getPhpName()." or Criteria object.
	 *
	 * @param      mixed \$values Criteria or ".$table->getPhpName()." object containing data that is used to create the INSERT statement.
	 * @param      Connection \$con the connection to use
	 * @return     mixed The new primary key.
	 * @throws     PropelException Any exceptions caught during processing will be
	 *		 rethrown wrapped into a PropelException.
	 */
	public static function doInsert(\$values, \$con = null)
	{
		if (\$con === null) {
			\$con = Propel::getConnection(self::DATABASE_NAME);
		}

		if (\$values instanceof Criteria) {
			\$criteria = clone \$values; // rename for clarity
		} else {
			\$criteria = \$values->buildCriteria(); // build Criteria from ".$table->getPhpName()." object
		}
";

		foreach ($table->getColumns() as $col) {
			$cfc = $col->getPhpName();
			if ($col->isPrimaryKey() && $col->isAutoIncrement() && $table->getIdMethod() != "none") {
				$script .= "
		\$criteria->remove(".$this->getColumnConstant($col)."); // remove pkey col since this table uses auto-increment
";
			}
		}
		$script .= "

		// Set the correct dbName
		\$criteria->setDbName(self::DATABASE_NAME);

		try {
			// use transaction because \$criteria could contain info
			// for more than one table (I guess, conceivably)
			\$con->begin();
			\$pk = ".$this->basePeerClassname."::doInsert(\$criteria, \$con);
			\$con->commit();
		} catch(PropelException \$e) {
			\$con->rollback();
			throw \$e;
		}

		return \$pk;
	}
";
	}

	/**
	 * Adds the doUpdate() method.
	 * @param      string &$script The script will be modified in this method.
	 */
	protected function addDoUpdate(&$script)
	{
		$table = $this->getTable();
		$script .= "
	/**
	 * Method perform an UPDATE on the database, given a ".$table->getPhpName()." or Criteria object.
	 *
	 * @param      mixed \$values Criteria or ".$table->getPhpName()." object containing data that is used to create the UPDATE statement.
	 * @param      Connection \$con The connection to use (specify Connection object to exert more control over transactions).
	 * @return     int The number of affected rows (if supported by underlying database driver).
	 * @throws     PropelException Any exceptions caught during processing will be
	 *		 rethrown wrapped into a PropelException.
	 */
	public static function doUpdate(\$values, \$con = null)
	{
		if (\$con === null) {
			\$con = Propel::getConnection(self::DATABASE_NAME);
		}

		\$selectCriteria = new Criteria(self::DATABASE_NAME);

		if (\$values instanceof Criteria) {
			\$criteria = clone \$values; // rename for clarity
";
		foreach ($table->getColumns() as $col) {
			if($col->isPrimaryKey()) {
				$script .= "
			\$comparison = \$criteria->getComparison(".$this->getColumnConstant($col).");
			\$selectCriteria->add(".$this->getColumnConstant($col).", \$criteria->remove(".$this->getColumnConstant($col)."), \$comparison);
";
			}  /* if col is prim key */
	 	} /* foreach */

		$script .= "
		} else { // \$values is ".$table->getPhpName()." object
			\$criteria = \$values->buildCriteria(); // gets full criteria
			\$selectCriteria = \$values->buildPkeyCriteria(); // gets criteria w/ primary key(s)
		}

		// set the correct dbName
		\$criteria->setDbName(self::DATABASE_NAME);

		return {$this->basePeerClassname}::doUpdate(\$selectCriteria, \$criteria, \$con);
	}
";
	}

	/**
	 * Adds the doDeleteAll() method.
	 * @param      string &$script The script will be modified in this method.
	 */
	protected function addDoDeleteAll(&$script)
	{
		$table = $this->getTable();
		$script .= "
	/**
	 * Method to DELETE all rows from the ".$table->getName()." table.
	 *
	 * @return     int The number of affected rows (if supported by underlying database driver).
	 */
	public static function doDeleteAll(\$con = null)
	{
		if (\$con === null) {
			\$con = Propel::getConnection(self::DATABASE_NAME);
		}
		\$affectedRows = 0; // initialize var to track total num of affected rows
		try {
			// use transaction because \$criteria could contain info
			// for more than one table or we could emulating ON DELETE CASCADE, etc.
			\$con->begin();
			";
			if ($this->isDeleteCascadeEmulationNeeded()) {
				$script .="\$affectedRows += ".$this->getPeerClassname()."::doOnDeleteCascade(new Criteria(), \$con);
			";
			}
			if ($this->isDeleteSetNullEmulationNeeded()) {
				$script .= $this->getPeerClassname() . "::doOnDeleteSetNull(new Criteria(), \$con);
			";
			}
			$script .= "\$affectedRows += BasePeer::doDeleteAll(".$this->getPeerClassname()."::TABLE_NAME, \$con);
			\$con->commit();
			return \$affectedRows;
		} catch (PropelException \$e) {
			\$con->rollback();
			throw \$e;
		}
	}
";
	}

	/**
	 * Adds the doDelete() method.
	 * @param      string &$script The script will be modified in this method.
	 */
	protected function addDoDelete(&$script)
	{
		$table = $this->getTable();
		$script .= "
	/**
	 * Method perform a DELETE on the database, given a ".$table->getPhpName()." or Criteria object OR a primary key value.
	 *
	 * @param      mixed \$values Criteria or ".$table->getPhpName()." object or primary key or array of primary keys
	 *              which is used to create the DELETE statement
	 * @param      Connection \$con the connection to use
	 * @return     int 	The number of affected rows (if supported by underlying database driver).  This includes CASCADE-related rows
	 *				if supported by native driver or if emulated using Propel.
	 * @throws     PropelException Any exceptions caught during processing will be
	 *		 rethrown wrapped into a PropelException.
	 */
	 public static function doDelete(\$values, \$con = null)
	 {
		if (\$con === null) {
			\$con = Propel::getConnection(".$this->getPeerClassname()."::DATABASE_NAME);
		}

		if (\$values instanceof Criteria) {
			\$criteria = clone \$values; // rename for clarity
		} elseif (\$values instanceof ".$table->getPhpName().") {
";
		if (count($table->getPrimaryKey()) > 0) {
			$script .= "
			\$criteria = \$values->buildPkeyCriteria();";
		} else {
			$script .= "
			\$criteria = \$values->buildCriteria();";
		}

		$script .= "
		} else {
			// it must be the primary key
			\$criteria = new Criteria(self::DATABASE_NAME);";

		if (count($table->getPrimaryKey()) === 1) {
			$pkey = $table->getPrimaryKey();
			$col = array_shift($pkey);
			$script .= "
			\$criteria->add(".$this->getColumnConstant($col).", (array) \$values, Criteria::IN);";
		} else {
			$script .= "
			// primary key is composite; we therefore, expect
			// the primary key passed to be an array of pkey
			// values
			if(count(\$values) == count(\$values, COUNT_RECURSIVE))
			{
				// array is not multi-dimensional
				\$values = array(\$values);
			}
			\$vals = array();
			foreach(\$values as \$value)
			{
";
			$i=0;
			foreach($table->getPrimaryKey() as $col) {
				$script .= "
				\$vals[$i][] = \$value[$i];";
				$i++;
			}
			$script .= "
			}
";
			$i=0;
			foreach($table->getPrimaryKey() as $col) {
				$script .= "
			\$criteria->add(".$this->getColumnConstant($col).", \$vals[$i], Criteria::IN);";
				$i++;
			}
		} /* if count(table->getPrimaryKeys()) */

		$script .= "
		}

		// Set the correct dbName
		\$criteria->setDbName(self::DATABASE_NAME);

		\$affectedRows = 0; // initialize var to track total num of affected rows

		try {
			// use transaction because \$criteria could contain info
			// for more than one table or we could emulating ON DELETE CASCADE, etc.
			\$con->begin();
			";

		if ($this->isDeleteCascadeEmulationNeeded()) {
			$script .= "\$affectedRows += ".$this->getPeerClassname()."::doOnDeleteCascade(\$criteria, \$con);";
		}
		if ($this->isDeleteSetNullEmulationNeeded()) {
			$script .= $this->getPeerClassname() . "::doOnDeleteSetNull(\$criteria, \$con);";
		}

		$script .= "
			\$affectedRows += {$this->basePeerClassname}::doDelete(\$criteria, \$con);
			\$con->commit();
			return \$affectedRows;
		} catch (PropelException \$e) {
			\$con->rollback();
			throw \$e;
		}
	}
";
	}

	/**
	 * Adds the doOnDeleteCascade() method, which provides ON DELETE CASCADE emulation.
	 * @param      string &$script The script will be modified in this method.
	 */
	protected function addDoOnDeleteCascade(&$script)
	{
		$table = $this->getTable();
		$script .= "
	/**
	 * This is a method for emulating ON DELETE CASCADE for DBs that don't support this
	 * feature (like MySQL or SQLite).
	 *
	 * This method is not very speedy because it must perform a query first to get
	 * the implicated records and then perform the deletes by calling those Peer classes.
	 *
	 * This method should be used within a transaction if possible.
	 *
	 * @param      Criteria \$criteria
	 * @param      Connection \$con
	 * @return     int The number of affected rows (if supported by underlying database driver).
	 */
	protected static function doOnDeleteCascade(Criteria \$criteria, Connection \$con)
	{
		// initialize var to track total num of affected rows
		\$affectedRows = 0;

		// first find the objects that are implicated by the \$criteria
		\$objects = ".$this->getPeerClassname()."::doSelect(\$criteria, \$con);
		foreach(\$objects as \$obj) {
";

		foreach ($table->getReferrers() as $fk) {

			// $fk is the foreign key in the other table, so localTableName will
			// actually be the table name of other table
			$tblFK = $fk->getTable();

			$joinedTablePeerBuilder = OMBuilder::getNewPeerBuilder($tblFK);
			$tblFKPackage = $joinedTablePeerBuilder->getStubPeerBuilder()->getPackage();

			if (!$tblFK->isForReferenceOnly()) {
				// we can't perform operations on tables that are
				// not within the schema (i.e. that we have no map for, etc.)

				$fkClassName = $tblFK->getPhpName();

				// i'm not sure whether we can allow delete cascade for foreign keys
				// within the same table?  perhaps we can?
				if ( $fk->getOnDelete() == ForeignKey::CASCADE && $tblFK->getName() != $table->getName()) {

					// backwards on purpose
					$columnNamesF = $fk->getLocalColumns();
					$columnNamesL = $fk->getForeignColumns();

					$script .= "

			include_once '".$this->getFilePath($tblFKPackage, $tblFK->getPhpName())."';

			// delete related $fkClassName objects
			\$c = new Criteria();
			";
					for($x=0,$xlen=count($columnNamesF); $x < $xlen; $x++) {
						$columnFK = $tblFK->getColumn($columnNamesF[$x]);
						$columnL = $table->getColumn($columnNamesL[$x]);

						$script .= "
			\$c->add(".$joinedTablePeerBuilder->getColumnConstant($columnFK) .", \$obj->get".$columnL->getPhpName()."());";
					}

					$script .= "
			\$affectedRows += ".$joinedTablePeerBuilder->getPeerClassname()."::doDelete(\$c, \$con);";

				} // if cascade && fkey table name != curr table name

			} // if not for ref only
		} // foreach foreign keys
			$script .= "
		}
		return \$affectedRows;
	}
";
	} // end addDoOnDeleteCascade

	/**
	 * Adds the doOnDeleteSetNull() method, which provides ON DELETE SET NULL emulation.
	 * @param      string &$script The script will be modified in this method.
	 */
	protected function addDoOnDeleteSetNull(&$script)
	{
		$table = $this->getTable();
		$script .= "
	/**
	 * This is a method for emulating ON DELETE SET NULL DBs that don't support this
	 * feature (like MySQL or SQLite).
	 *
	 * This method is not very speedy because it must perform a query first to get
	 * the implicated records and then perform the deletes by calling those Peer classes.
	 *
	 * This method should be used within a transaction if possible.
	 *
	 * @param      Criteria \$criteria
	 * @param      Connection \$con
	 * @return     void
	 */
	protected static function doOnDeleteSetNull(Criteria \$criteria, Connection \$con)
	{

		// first find the objects that are implicated by the \$criteria
		\$objects = ".$this->getPeerClassname()."::doSelect(\$criteria, \$con);
		foreach(\$objects as \$obj) {
";

		// This logic is almost exactly the same as that in doOnDeleteCascade()
		// it may make sense to refactor this, provided that thigns don't
		// get too complicated.

		foreach ($table->getReferrers() as $fk) {

			// $fk is the foreign key in the other table, so localTableName will
			// actually be the table name of other table
			$tblFK = $fk->getTable();
			$refTablePeerBuilder = OMBuilder::getNewPeerBuilder($tblFK);

			if (!$tblFK->isForReferenceOnly()) {
				// we can't perform operations on tables that are
				// not within the schema (i.e. that we have no map for, etc.)

				$fkClassName = $tblFK->getPhpName();

				// i'm not sure whether we can allow delete setnull for foreign keys
				// within the same table?  perhaps we can?
				if ( $fk->getOnDelete() == ForeignKey::SETNULL &&
						$fk->getTable()->getName() != $table->getName()) {

							// backwards on purpose
							$columnNamesF = $fk->getLocalColumns();
							$columnNamesL = $fk->getForeignColumns(); // should be same num as foreign
					$script .= "
			// set fkey col in related $fkClassName rows to NULL
			\$selectCriteria = new Criteria(".$this->getPeerClassname()."::DATABASE_NAME);
			\$updateValues = new Criteria(".$this->getPeerClassname()."::DATABASE_NAME);";

					for ($x=0,$xlen=count($columnNamesF); $x < $xlen; $x++) {
						$columnFK = $tblFK->getColumn($columnNamesF[$x]);
						$columnL = $table->getColumn($columnNamesL[$x]);
						$script .= "
			\$selectCriteria->add(".$refTablePeerBuilder->getColumnConstant($columnFK).", \$obj->get".$columnL->getPhpName()."());
			\$updateValues->add(".$refTablePeerBuilder->getColumnConstant($columnFK).", null);
";
					}

					$script .= "
			{$this->basePeerClassname}::doUpdate(\$selectCriteria, \$updateValues, \$con); // use BasePeer because generated Peer doUpdate() methods only update using pkey
";
				} // if setnull && fkey table name != curr table name
			} // if not for ref only
		} // foreach foreign keys

		$script .= "
		}
	}
";
	}

	/**
	 * Adds the doValidate() method.
	 * @param      string &$script The script will be modified in this method.
	 */
	protected function addDoValidate(&$script)
	{
		$table = $this->getTable();
		$script .= "
	/**
	 * Validates all modified columns of given ".$table->getPhpName()." object.
	 * If parameter \$columns is either a single column name or an array of column names
	 * than only those columns are validated.
	 *
	 * NOTICE: This does not apply to primary or foreign keys for now.
	 *
	 * @param      ".$table->getPhpName()." \$obj The object to validate.
	 * @param      mixed \$cols Column name or array of column names.
	 *
	 * @return     mixed TRUE if all columns are valid or the error message of the first invalid column.
	 */
	public static function doValidate(".$table->getPhpName()." \$obj, \$cols = null)
	{
		\$columns = array();

		if (\$cols) {
			\$dbMap = Propel::getDatabaseMap(".$this->getPeerClassname()."::DATABASE_NAME);
			\$tableMap = \$dbMap->getTable(".$this->getPeerClassname()."::TABLE_NAME);

			if (! is_array(\$cols)) {
				\$cols = array(\$cols);
			}

			foreach(\$cols as \$colName) {
				if (\$tableMap->containsColumn(\$colName)) {
					\$get = 'get' . \$tableMap->getColumn(\$colName)->getPhpName();
					\$columns[\$colName] = \$obj->\$get();
				}
			}
		} else {
";
		foreach ($table->getValidators() as $val) {
			$col = $val->getColumn();
			if (!$col->isAutoIncrement()) {
				$script .= "
		if (\$obj->isNew() || \$obj->isColumnModified(".$this->getColumnConstant($col)."))
			\$columns[".$this->getColumnConstant($col)."] = \$obj->get".$col->getPhpName()."();
";
			} // if
  		} // foreach

  		$script .= "
		}

		return {$this->basePeerClassname}::doValidate(".$this->getPeerClassname()."::DATABASE_NAME, ".$this->getPeerClassname()."::TABLE_NAME, \$columns);
	}
";
	} // end addDoValidate()

	/**
	 * Adds the retrieveByPK method for tables with single-column primary key.
	 * @param      string &$script The script will be modified in this method.
	 */
	protected function addRetrieveByPK_SinglePK(&$script)
	{
		$table = $this->getTable();
		$script .= "
	/**
	 * Retrieve a single object by pkey.
	 *
	 * @param      mixed \$pk the primary key.
	 * @param      Connection \$con the connection to use
	 * @return     " . $table->getPhpName() . "
	 */
	public static function ".$this->getRetrieveMethodName()."(\$pk, \$con = null)
	{
		if (\$con === null) {
			\$con = Propel::getConnection(self::DATABASE_NAME);
		}

		\$criteria = new Criteria(".$this->getPeerClassname()."::DATABASE_NAME);
";
		if (count($table->getPrimaryKey()) === 1) {
			$pkey = $table->getPrimaryKey();
			$col = array_shift($pkey);
			$script .= "
		\$criteria->add(".$this->getColumnConstant($col).", \$pk);
";
		} else {
			// primary key is composite; we therefore, expect
			// the primary key passed to be an array of pkey
			// values
			$i=0;
			foreach($table->getPrimaryKey() as $col) {
	   			$script .= "
		\$criteria->add(".$this->getColumnConstant($col).", \$pk[$i]);";
				$i++;
			}
		} /* if count(table.PrimaryKeys) */
		$script .= "

		\$v = ".$this->getPeerClassname()."::doSelect(\$criteria, \$con);

		return !empty(\$v) > 0 ? \$v[0] : null;
	}
";
	}

	/**
	 * Adds the retrieveByPKs method for tables with single-column primary key.
	 * @param      string &$script The script will be modified in this method.
	 */
	protected function addRetrieveByPKs_SinglePK(&$script)
	{
		$table = $this->getTable();
		$script .= "
	/**
	 * Retrieve multiple objects by pkey.
	 *
	 * @param      array \$pks List of primary keys
	 * @param      Connection \$con the connection to use
	 * @throws     PropelException Any exceptions caught during processing will be
	 *		 rethrown wrapped into a PropelException.
	 */
	public static function ".$this->getRetrieveMethodName()."s(\$pks, \$con = null)
	{
		if (\$con === null) {
			\$con = Propel::getConnection(self::DATABASE_NAME);
		}

		\$objs = null;
		if (empty(\$pks)) {
			\$objs = array();
		} else {
			\$criteria = new Criteria();";
		if (count($table->getPrimaryKey()) == 1) {
			$k1 = $table->getPrimaryKey();
			$script .= "
			\$criteria->add(".$this->getColumnConstant($k1[0]).", \$pks, Criteria::IN);";
		} else {
			$script .= "
			foreach(\$pks as \$pk) {";
			$i = 0;
			foreach($table->getPrimaryKey() as $col) {
				$script .= "
				\$c{$i} = \$criteria->getNewCriterion(".$this->getPeerClassname($col).", \$pk[$i], Criteria::EQUAL);";
				$j = $i - 1;
				if ($i > 0) {
					$script .= "
				\$c{$j}->addAnd(\$c{$i});";
				} /* if $i > 0 */
				$i++;
			} /* foreach */

			$script .= "

				\$criteria->addOr(\$c0);
			}";
		} /* if count prim keys == 1 */
		$script .= "
			\$objs = ".$this->getPeerClassname()."::doSelect(\$criteria, \$con);
		}
		return \$objs;
	}
";
	}

	/**
	 * Adds the retrieveByPK method for tables with multi-column primary key.
	 * @param      string &$script The script will be modified in this method.
	 */
	protected function addRetrieveByPK_MultiPK(&$script)
	{
		$table = $this->getTable();
		$script .= "
	/**
	 * Retrieve object using using composite pkey values.
	 * ";
		foreach ($table->getPrimaryKey() as $col) {
			$clo = strtolower($col->getName());
			$cptype = $col->getPhpNative();
			$script .= "@param $cptype $".$clo."
	   ";
	   }
	   $script .= "
	 * @param      Connection \$con
	 * @return     ".$table->getPhpName()."
	 */
	public static function ".$this->getRetrieveMethodName()."(";
		$co = 0;
		foreach ($table->getPrimaryKey() as $col) {
			$clo = strtolower($col->getName());
			$script .= ($co++ ? "," : "") . " $".$clo;
		} /* foreach */
		$script .= ", \$con = null) {
		if (\$con === null) {
			\$con = Propel::getConnection(self::DATABASE_NAME);
		}
		\$criteria = new Criteria();";
		foreach ($table->getPrimaryKey() as $col) {
			$clo = strtolower($col->getName());
			$script .= "
		\$criteria->add(".$this->getColumnConstant($col).", $".$clo.");";
		}
		$script .= "
		\$v = ".$this->getPeerClassname()."::doSelect(\$criteria, \$con);

		return !empty(\$v) ? \$v[0] : null;
	}";
	}

	/**
	 * Adds the getTableMap() method which is a convenience method for apps to get DB metadata.
	 * @param      string &$script The script will be modified in this method.
	 */
	protected function addGetTableMap(&$script)
	{
		$script .= "
	/**
	 * Returns the TableMap related to this peer.
	 * This method is not needed for general use but a specific application could have a need.
	 * @return     TableMap
	 * @throws     PropelException Any exceptions caught during processing will be
	 *		 rethrown wrapped into a PropelException.
	 */
	public static function getTableMap()
	{
		return Propel::getDatabaseMap(self::DATABASE_NAME)->getTable(self::TABLE_NAME);
	}
";

	}
} // PHP5BasicPeerBuilder
