<?php

/*
 *  $Id: PHP5NestedSetPeerBuilder.php 1096 2009-02-09 13:36:21Z heltem $
 *
 * 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 tree nested set Peer class for user object model (OM).
 *
 * This class produces the base tree nested set object class (e.g. BaseMyTable) which contains all
 * the custom-built accessor and setter methods.
 *
 * This class replaces the Node.tpl, with the intent of being easier for users
 * to customize (through extending & overriding).
 *
 * @author     heltem <heltem@o2php.com>
 * @package    propel.engine.builder.om.php5
 */
class PHP5NestedSetPeerBuilder extends PeerBuilder {

	/**
	 * 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 getUnprefixedClassname()
	{
		return $this->getBuildProperty('basePrefix') . $this->getStubObjectBuilder()->getUnprefixedClassname() . 'NestedSetPeer';
	}

	/**
	 * 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)
	{
		$script .="
require '".$this->getPeerBuilder()->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();

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

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

		// FIXME
		// - Probably the build needs to be customized for supporting
		// tables that are "aliases".  -- definitely a fringe usecase, though.

		$this->addConstants($script);

		$this->addCreateRoot($script);

		$this->addRetrieveRoot($script);

		$this->addInsertAsFirstChildOf($script);
		$this->addInsertAsLastChildOf($script);
		$this->addInsertAsPrevSiblingOf($script);
		$this->addInsertAsNextSiblingOf($script);
		$this->addInsertAsParentOf($script);

		$this->addInsertRoot($script);
		$this->addInsertParent($script);

		$this->addDeleteRoot($script);
		$this->addDeleteNode($script);

		$this->addMoveToFirstChildOf($script);
		$this->addMoveToLastChildOf($script);
		$this->addMoveToPrevSiblingOf($script);
		$this->addMoveToNextSiblingOf($script);

		$this->addRetrieveFirstChild($script);
		$this->addRetrieveLastChild($script);
		$this->addRetrievePrevSibling($script);
		$this->addRetrieveNextSibling($script);

		$this->addRetrieveTree($script);
		$this->addRetrieveBranch($script);
		$this->addRetrieveChildren($script);
		$this->addRetrieveDescendants($script);
		$this->addRetrieveSiblings($script);
		$this->addRetrieveParent($script);

		$this->addGetLevel($script);
		$this->addGetNumberOfChildren($script);
		$this->addGetNumberOfDescendants($script);
		$this->addGetPath($script);

		$this->addIsValid($script);
		$this->addIsRoot($script);
		$this->addIsLeaf($script);
		$this->addIsChildOf($script);
		$this->addIsChildOfOrSiblingTo($script);
		$this->addIsEqualTo($script);

		$this->addHasParent($script);
		$this->addHasPrevSibling($script);
		$this->addHasNextSibling($script);
		$this->addHasChildren($script);

		$this->addDeleteDescendants($script);

		$this->addGetNode($script);

		$this->addHydrateDescendants($script);
		$this->addHydrateChildren($script);

		$this->addShiftRParent($script);
		$this->addUpdateLoadedNode($script);
		$this->addUpdateDBNode($script);

		$this->addShiftRLValues($script);
		$this->addShiftRLRange($script);
	}

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

	protected function addConstants(&$script)
	{
		$table = $this->getTable();
		$tableName = $table->getName();

		$colname = array();

		foreach ($table->getColumns() as $col) {
			if ($col->isNestedSetLeftKey()) {
				$colname['left'] = $this->prefixTablename($tableName) . '.' . strtoupper($col->getName());
			}

			if ($col->isNestedSetRightKey()) {
				$colname['right'] = $this->prefixTablename($tableName) . '.' . strtoupper($col->getName());
			}

			if ($col->isTreeScopeKey()) {
				$colname['scope'] = $this->prefixTablename($tableName) . '.' . strtoupper($col->getName());
			}

			if (3 == count($colname)) {
				break;
			}
		}

		if(!isset($colname['left'])) {
			throw new EngineException("One column must have nestedSetLeftKey attribute set to true for [" . $table->getName() . "] table");
		}

		if(!isset($colname['right'])) {
			throw new EngineException("One column must have nestedSetRightKey attribute set to true for [" . $table->getName() . "] table");
		}

		$colname['scope'] = isset($colname['scope']) ? $colname['scope'] : null;

		$script .= "
	/**
	 * Left column for the set
	 */
	const LEFT_COL = " . var_export($colname['left'], true) . ";

	/**
	 * Right column for the set
	 */
	const RIGHT_COL = " . var_export($colname['right'], true) . ";

	/**
	 * Scope column for the set
	 */
	const SCOPE_COL = " . var_export($colname['scope'], true) . ";
";
	}

	protected function addCreateRoot(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Creates the supplied node as the root node.
	 *
	 * @param      $objectClassname \$node	Propel object for model
	 * @throws     PropelException
	 */
	public static function createRoot(NodeObject \$node)
	{
		if (\$node->getLeftValue()) {
			throw new PropelException('Cannot turn an existing node into a root node.');
		}

		\$node->setLeftValue(1);
		\$node->setRightValue(2);
	}
";
	}

	protected function addRetrieveRoot(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Returns the root node for a given scope id
	 *
	 * @param      int \$scopeId		Scope id to determine which root node to return
	 * @param      PropelPDO \$con	Connection to use.
	 * @return     $objectClassname			Propel object for root node
	 */
	public static function retrieveRoot(\$scopeId = null, PropelPDO \$con = null)
	{
		\$c = new Criteria($peerClassname::DATABASE_NAME);

		\$c->add(self::LEFT_COL, 1, Criteria::EQUAL);

		if (self::SCOPE_COL) {
			\$c->add(self::SCOPE_COL, \$scopeId, Criteria::EQUAL);
		}

		return $peerClassname::doSelectOne(\$c, \$con);
	}
";
	}

	protected function addInsertAsFirstChildOf(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Inserts \$child as first child of given \$parent node
	 *
	 * @param      $objectClassname \$child	Propel object for child node
	 * @param      $objectClassname \$parent	Propel object for parent node
	 * @param      PropelPDO \$con	Connection to use.
	 * @return     void
	 */
	public static function insertAsFirstChildOf(NodeObject \$child, NodeObject \$parent, PropelPDO \$con = null)
	{
		// Update \$child node properties
		\$child->setLeftValue(\$parent->getLeftValue() + 1);
		\$child->setRightValue(\$parent->getLeftValue() + 2);
		\$child->setParentNode(\$parent);

		\$sidv = null;
		if (self::SCOPE_COL) {
			\$child->setScopeIdValue(\$sidv = \$parent->getScopeIdValue());
		}

		// Update database nodes
		self::shiftRLValues(\$child->getLeftValue(), 2, \$con, \$sidv);

		// Update all loaded nodes
		self::updateLoadedNode(\$parent, 2, \$con);
	}
";
	}

	protected function addInsertAsLastChildOf(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Inserts \$child as last child of destination node \$parent
	 *
	 * @param      $objectClassname \$child		Propel object for child node
	 * @param      $objectClassname \$parent	Propel object for parent node
	 * @param      PropelPDO \$con	Connection to use.
	 * @return     void
	 */
	public static function insertAsLastChildOf(NodeObject \$child, NodeObject \$parent, PropelPDO \$con = null)
	{
		// Update \$child node properties
		\$child->setLeftValue(\$parent->getRightValue());
		\$child->setRightValue(\$parent->getRightValue() + 1);
		\$child->setParentNode(\$parent);

		\$sidv = null;
		if (self::SCOPE_COL) {
			\$child->setScopeIdValue(\$sidv = \$parent->getScopeIdValue());
		}

		// Update database nodes
		self::shiftRLValues(\$child->getLeftValue(), 2, \$con, \$sidv);

		// Update all loaded nodes
		self::updateLoadedNode(\$parent, 2, \$con);
	}
";
	}

	protected function addInsertAsPrevSiblingOf(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Inserts \$sibling as previous sibling to destination node \$node
	 *
	 * @param      $objectClassname \$node		Propel object for destination node
	 * @param      $objectClassname \$sibling	Propel object for source node
	 * @param      PropelPDO \$con	Connection to use.
	 * @return     void
	 */
	public static function insertAsPrevSiblingOf(NodeObject \$node, NodeObject \$sibling, PropelPDO \$con = null)
	{
		if (\$sibling->isRoot()) {
			throw new PropelException('Root nodes cannot have siblings');
		}

		\$node->setLeftValue(\$sibling->getLeftValue());
		\$node->setRightValue(\$sibling->getLeftValue() + 1);
		\$node->setParentNode(\$sibling->retrieveParent());

		\$sidv = null;
		if (self::SCOPE_COL) {
			\$node->setScopeIdValue(\$sidv = \$sibling->getScopeIdValue());
		}

		// Update database nodes
		self::shiftRLValues(\$node->getLeftValue(), 2, \$con, \$sidv);

		// Update all loaded nodes
		self::updateLoadedNode(\$sibling->retrieveParent(), 2, \$con);
	}
";
	}

	protected function addInsertAsNextSiblingOf(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Inserts \$sibling as next sibling to destination node \$node
	 *
	 * @param      $objectClassname \$node		Propel object for destination node
	 * @param      $objectClassname \$sibling	Propel object for source node
	 * @param      PropelPDO \$con	Connection to use.
	 * @return     void
	 */
	public static function insertAsNextSiblingOf(NodeObject \$node, NodeObject \$sibling, PropelPDO \$con = null)
	{
		if (\$sibling->isRoot()) {
			throw new PropelException('Root nodes cannot have siblings');
		}

		\$node->setLeftValue(\$sibling->getRightValue() + 1);
		\$node->setRightValue(\$sibling->getRightValue() + 2);
		\$node->setParentNode(\$sibling->retrieveParent());

		\$sidv = null;
		if (self::SCOPE_COL) {
			\$node->setScopeIdValue(\$sidv = \$sibling->getScopeIdValue());
		}

		// Update database nodes
		self::shiftRLValues(\$node->getLeftValue(), 2, \$con, \$sidv);

		// Update all loaded nodes
		self::updateLoadedNode(\$sibling->retrieveParent(), 2, \$con);
	}
";
	}

	protected function addInsertAsParentOf(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Inserts \$parent as parent of given node.
	 *
	 * @param      $objectClassname \$parent	Propel object for given parent node
	 * @param      $objectClassname \$node	Propel object for given destination node
	 * @param      PropelPDO \$con	Connection to use.
	 * @return     void
	 */
	public static function insertAsParentOf(NodeObject \$parent, NodeObject \$node, PropelPDO \$con = null)
	{
		\$sidv = null;
		if (self::SCOPE_COL) {
			\$sidv = \$node->getScopeIdValue();
		}

		self::shiftRLValues(\$node->getLeftValue(), 1, \$con, \$sidv);
		self::shiftRLValues(\$node->getRightValue() + 2, 1, \$con, \$sidv);

		if (self::SCOPE_COL) {
			\$parent->setScopeIdValue(\$sidv);
		}

		\$parent->setLeftValue(\$node->getLeftValue());
		\$parent->setRightValue(\$node->getRightValue() + 2);

		\$previous_parent = \$node->retrieveParent();
		\$parent->setParentNode(\$previous_parent);
		\$node->setParentNode(\$parent);

		\$node->save(\$con);

		// Update all loaded nodes
		self::updateLoadedNode(\$previous_parent, 2, \$con);
	}
";
	}

	protected function addInsertRoot(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Inserts \$node as root node
	 *
	 * @param      $objectClassname \$node	Propel object as root node
	 * @param      PropelPDO \$con	Connection to use.
	 * @return     void
	 */
	public static function insertRoot(NodeObject \$node, PropelPDO \$con = null)
	{
		\$sidv = null;
		if (self::SCOPE_COL) {
			\$sidv = \$node->getScopeIdValue();
		}

		$peerClassname::insertAsParentOf($peerClassname::retrieveRoot(\$sidv, \$con), \$node, \$con);
	}
";
	}

	protected function addInsertParent(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Inserts \$parent as parent to destination node \$child
	 *
	 * @deprecated 1.3 - 2007/11/06
	 * @see        insertAsParentOf()
	 * @param      $objectClassname \$child	Propel object to become child node
	 * @param      $objectClassname \$parent	Propel object as parent node
	 * @param      PropelPDO \$con	Connection to use.
	 * @return     void
	 */
	public static function insertParent(NodeObject \$child, NodeObject \$parent, PropelPDO \$con = null)
	{
		self::insertAsParentOf(\$parent, \$child, \$con);
	}
";
	}

	protected function addDeleteRoot(&$script)
	{
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Delete root node
	 *
	 * @param      PropelPDO \$con	Connection to use.
	 * @return     boolean		Deletion status
	 */
	public static function deleteRoot(\$scopeId = null, PropelPDO \$con = null)
	{
		if (!self::SCOPE_COL) {
			\$scopeId = null;
		}
		\$root = $peerClassname::retrieveRoot(\$scopeId, \$con);
		if ($peerClassname::getNumberOfChildren(\$root) == 1) {
			return $peerClassname::deleteNode(\$root, \$con);
		} else {
			return false;
		}
	}
";
	}

	protected function addDeleteNode(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Delete \$dest node
	 *
	 * @param      $objectClassname \$dest	Propel object node to delete
	 * @param      PropelPDO \$con	Connection to use.
	 * @return     boolean		Deletion status
	 */
	public static function deleteNode(NodeObject \$dest, PropelPDO \$con = null)
	{
		if (\$dest->getLeftValue() == 1) {
			// deleting root implies conditions (see deleteRoot() method)
			return $peerClassname::deleteRoot(\$con);
		}

		\$sidv = null;
		if (self::SCOPE_COL) {
			\$sidv = \$dest->getScopeIdValue();
		}

		self::shiftRLRange(\$dest->getLeftValue(), \$dest->getRightValue(), -1, \$con, \$sidv);
		self::shiftRLValues(\$dest->getRightValue() + 1, -2, \$con, \$sidv);
		return \$dest->delete(\$con);
	}
";
	}

	protected function addMoveToFirstChildOf(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Moves \$child to be first child of \$parent
	 *
	 * @param      $objectClassname \$parent	Propel object for parent node
	 * @param      $objectClassname \$child		Propel object for child node
	 * @param      PropelPDO \$con	Connection to use.
	 * @return     void
	 */
	public static function moveToFirstChildOf(NodeObject \$parent, NodeObject \$child, PropelPDO \$con = null)
	{
		if (\$parent->getScopeIdValue() != \$child->getScopeIdValue()) {
			throw new PropelException('Moving nodes across trees is not supported');
		}
		\$destLeft = \$parent->getLeftValue() + 1;
		self::updateDBNode(\$child, \$destLeft, \$con);

		// Update all loaded nodes
		self::updateLoadedNode(\$parent, 2, \$con);
	}
";
	}

	protected function addMoveToLastChildOf(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Moves \$child to be last child of \$parent
	 *
	 * @param      $objectClassname \$parent	Propel object for parent node
	 * @param      $objectClassname \$child		Propel object for child node
	 * @param      PropelPDO \$con	Connection to use.
	 * @return     void
	 */
	public static function moveToLastChildOf(NodeObject \$parent, NodeObject \$child, PropelPDO \$con = null)
	{
		if (\$parent->getScopeIdValue() != \$child->getScopeIdValue()) {
			throw new PropelException('Moving nodes across trees is not supported');
		}
		\$destLeft = \$parent->getRightValue();
		self::updateDBNode(\$child, \$destLeft, \$con);

		// Update all loaded nodes
		self::updateLoadedNode(\$parent, 2, \$con);
	}
";
	}

	protected function addMoveToPrevSiblingOf(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Moves \$node to be prev sibling to \$dest
	 *
	 * @param      $objectClassname \$dest	Propel object for destination node
	 * @param      $objectClassname \$node	Propel object for source node
	 * @param      PropelPDO \$con	Connection to use.
	 * @return     void
	 */
	public static function moveToPrevSiblingOf(NodeObject \$dest, NodeObject \$node, PropelPDO \$con = null)
	{
		if (\$dest->getScopeIdValue() != \$node->getScopeIdValue()) {
			throw new PropelException('Moving nodes across trees is not supported');
		}
		\$destLeft = \$dest->getLeftValue();
		self::updateDBNode(\$node, \$destLeft, \$con);

		// Update all loaded nodes
		self::updateLoadedNode(\$dest->retrieveParent(), 2, \$con);
	}
";
	}

	protected function addMoveToNextSiblingOf(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Moves \$node to be next sibling to \$dest
	 *
	 * @param      $objectClassname \$dest	Propel object for destination node
	 * @param      $objectClassname \$node	Propel object for source node
	 * @param      PropelPDO \$con	Connection to use.
	 * @return     void
	 */
	public static function moveToNextSiblingOf(NodeObject \$dest, NodeObject \$node, PropelPDO \$con = null)
	{
		if (\$dest->getScopeIdValue() != \$node->getScopeIdValue()) {
			throw new PropelException('Moving nodes across trees is not supported');
		}
		\$destLeft = \$dest->getRightValue();
		\$destLeft = \$destLeft + 1;
		self::updateDBNode(\$node, \$destLeft, \$con);

		// Update all loaded nodes
		self::updateLoadedNode(\$dest->retrieveParent(), 2, \$con);
	}
";
	}

	protected function addRetrieveFirstChild(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Gets first child for the given node if it exists
	 *
	 * @param      $objectClassname \$node	Propel object for src node
	 * @param      PropelPDO \$con	Connection to use.
	 * @return     mixed 		Propel object if exists else false
	 */
	public static function retrieveFirstChild(NodeObject \$node, PropelPDO \$con = null)
	{
		\$c = new Criteria($peerClassname::DATABASE_NAME);
		\$c->add(self::LEFT_COL, \$node->getLeftValue() + 1, Criteria::EQUAL);
		if (self::SCOPE_COL) {
			\$c->add(self::SCOPE_COL, \$node->getScopeIdValue(), Criteria::EQUAL);
		}

		return $peerClassname::doSelectOne(\$c, \$con);
	}
";
	}

	protected function addRetrieveLastChild(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Gets last child for the given node if it exists
	 *
	 * @param      $objectClassname \$node	Propel object for src node
	 * @param      PropelPDO \$con	Connection to use.
	 * @return     mixed 		Propel object if exists else false
	 */
	public static function retrieveLastChild(NodeObject \$node, PropelPDO \$con = null)
	{
		\$c = new Criteria($peerClassname::DATABASE_NAME);
		\$c->add(self::RIGHT_COL, \$node->getRightValue() - 1, Criteria::EQUAL);
		if (self::SCOPE_COL) {
			\$c->add(self::SCOPE_COL, \$node->getScopeIdValue(), Criteria::EQUAL);
		}

		return $peerClassname::doSelectOne(\$c, \$con);
	}
";
	}

	protected function addRetrievePrevSibling(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Gets prev sibling for the given node if it exists
	 *
	 * @param      $objectClassname \$node	Propel object for src node
	 * @param      PropelPDO \$con	Connection to use.
	 * @return     mixed 		Propel object if exists else null
	 */
	public static function retrievePrevSibling(NodeObject \$node, PropelPDO \$con = null)
	{
		\$c = new Criteria($peerClassname::DATABASE_NAME);
		\$c->add(self::RIGHT_COL, \$node->getLeftValue() - 1, Criteria::EQUAL);
		if (self::SCOPE_COL) {
			\$c->add(self::SCOPE_COL, \$node->getScopeIdValue(), Criteria::EQUAL);
		}
		\$prevSibling = $peerClassname::doSelectOne(\$c, \$con);
		\$node->setPrevSibling(\$prevSibling);
		return \$prevSibling;
	}
";
	}

	protected function addRetrieveNextSibling(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Gets next sibling for the given node if it exists
	 *
	 * @param      $objectClassname \$node	Propel object for src node
	 * @param      PropelPDO \$con	Connection to use.
	 * @return     mixed 		Propel object if exists else false
	 */
	public static function retrieveNextSibling(NodeObject \$node, PropelPDO \$con = null)
	{
		\$c = new Criteria($peerClassname::DATABASE_NAME);
		\$c->add(self::LEFT_COL, \$node->getRightValue() + 1, Criteria::EQUAL);
		if (self::SCOPE_COL) {
			\$c->add(self::SCOPE_COL, \$node->getScopeIdValue(), Criteria::EQUAL);
		}
		\$nextSibling = $peerClassname::doSelectOne(\$c, \$con);
		\$node->setNextSibling(\$nextSibling);
		return \$nextSibling;
	}
";
	}

	protected function addRetrieveTree(&$script)
	{
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Retrieves the entire tree from root
	 *
	 * @param      PropelPDO \$con	Connection to use.
	 */
	public static function retrieveTree(\$scopeId = null, PropelPDO \$con = null)
	{
		\$c = new Criteria($peerClassname::DATABASE_NAME);
		\$c->addAscendingOrderByColumn(self::LEFT_COL);
		if (self::SCOPE_COL) {
			\$c->add(self::SCOPE_COL, \$scopeId, Criteria::EQUAL);
		}
		\$stmt = $peerClassname::doSelectStmt(\$c, \$con);
		if (false !== (\$row = \$stmt->fetch(PDO::FETCH_NUM))) {
			\$omClass = $peerClassname::getOMClass(\$row, 0);
			\$cls = substr('.'.\$omClass, strrpos('.'.\$omClass, '.') + 1);

			\$key = ".$peerClassname."::getPrimaryKeyHashFromRow(\$row, 0);
			if (null === (\$root = ".$peerClassname."::getInstanceFromPool(\$key))) {
				" . $this->buildObjectInstanceCreationCode('$root', '$cls') . "
				\$root->hydrate(\$row);
			}

			\$root->setLevel(0);
			$peerClassname::hydrateDescendants(\$root, \$stmt);
			$peerClassname::addInstanceToPool(\$root);
			
			\$stmt->closeCursor();
			return \$root;
		}
		return false;
	}
";
	}

	protected function addRetrieveBranch(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Retrieves the entire tree from parent \$node
	 *
	 * @param      $objectClassname \$node	Propel object for parent node
	 * @param      PropelPDO \$con	Connection to use.
	 */
	public static function retrieveBranch(NodeObject \$node, PropelPDO \$con = null)
	{
		return $peerClassname::retrieveDescendants(\$node, \$con);
	}
";
	}

	protected function addRetrieveChildren(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Gets direct children for the node
	 *
	 * @param      $objectClassname \$node	Propel object for parent node
	 * @param      PropelPDO \$con	Connection to use.
	 */
	public static function retrieveChildren(NodeObject \$node, PropelPDO \$con = null)
	{
		\$c = new Criteria($peerClassname::DATABASE_NAME);
		\$c->addAscendingOrderByColumn(self::LEFT_COL);
		if (self::SCOPE_COL) {
			\$c->add(self::SCOPE_COL, \$node->getScopeIdValue(), Criteria::EQUAL);
		}
		\$c->add(self::LEFT_COL, \$node->getLeftValue(), Criteria::GREATER_THAN);
		\$c->addAnd(self::RIGHT_COL, \$node->getRightValue(), Criteria::LESS_THAN);
		\$stmt = $peerClassname::doSelectStmt(\$c, \$con);

		return $peerClassname::hydrateChildren(\$node, \$stmt);
	}
";
	}

	protected function addRetrieveDescendants(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Gets all descendants for the node
	 *
	 * @param      $objectClassname \$node	Propel object for parent node
	 * @param      PropelPDO \$con	Connection to use.
	 */
	public static function retrieveDescendants(NodeObject \$node, PropelPDO \$con = null)
	{
		\$c = new Criteria($peerClassname::DATABASE_NAME);
		\$c->addAscendingOrderByColumn(self::LEFT_COL);
		if (self::SCOPE_COL) {
			\$c->add(self::SCOPE_COL, \$node->getScopeIdValue(), Criteria::EQUAL);
		}
		\$c->add(self::LEFT_COL, \$node->getLeftValue(), Criteria::GREATER_THAN);
		\$c->addAnd(self::RIGHT_COL, \$node->getRightValue(), Criteria::LESS_THAN);
		\$stmt = $peerClassname::doSelectStmt(\$c, \$con);

		return $peerClassname::hydrateDescendants(\$node, \$stmt);
	}
";
	}

	protected function addRetrieveSiblings(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Gets all siblings for the node
	 *
	 * @param      $objectClassname \$node	Propel object for src node
	 * @param      PropelPDO \$con	Connection to use.
	 */
	public static function retrieveSiblings(NodeObject \$node, PropelPDO \$con = null)
	{
		\$parent = $peerClassname::retrieveParent(\$node, \$con);
		\$siblings = $peerClassname::retrieveChildren(\$parent, \$con);

		return \$siblings;
	}
";
	}

	protected function addRetrieveParent(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Gets immediate ancestor for the given node if it exists
	 *
	 * @param      $objectClassname \$node	Propel object for src node
	 * @param      PropelPDO \$con	Connection to use.
	 * @return     mixed 		Propel object if exists else null
	 */
	public static function retrieveParent(NodeObject \$node, PropelPDO \$con = null)
	{
		\$c = new Criteria($peerClassname::DATABASE_NAME);
		\$c1 = \$c->getNewCriterion(self::LEFT_COL, \$node->getLeftValue(), Criteria::LESS_THAN);
		\$c2 = \$c->getNewCriterion(self::RIGHT_COL, \$node->getRightValue(), Criteria::GREATER_THAN);

		\$c1->addAnd(\$c2);

		\$c->add(\$c1);
		if (self::SCOPE_COL) {
			\$c->add(self::SCOPE_COL, \$node->getScopeIdValue(), Criteria::EQUAL);
		}
		\$c->addAscendingOrderByColumn(self::RIGHT_COL);

		\$parent = $peerClassname::doSelectOne(\$c, \$con);

		\$node->setParentNode(\$parent);

		return \$parent;
	}
";
	}

	protected function addGetLevel(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Gets level for the given node
	 *
	 * @param      $objectClassname \$node	Propel object for src node
	 * @param      PropelPDO \$con	Connection to use.
	 * @return     int			Level for the given node
	 */
	public static function getLevel(NodeObject \$node, PropelPDO \$con = null)
	{
		if (\$con === null) {
			\$con = Propel::getConnection($peerClassname::DATABASE_NAME, Propel::CONNECTION_READ);
		}

		\$sql = \"SELECT COUNT(*) AS level FROM \" . self::TABLE_NAME . \" WHERE \" . self::LEFT_COL . \" < :left AND \" . self::RIGHT_COL . \" > :right\";

		if (self::SCOPE_COL) {
			\$sql .= ' AND ' . self::SCOPE_COL . ' = :scope';
		}

		\$stmt = \$con->prepare(\$sql);
		\$stmt->bindValue(':left', \$node->getLeftValue(), PDO::PARAM_INT);
		\$stmt->bindValue(':right', \$node->getRightValue(), PDO::PARAM_INT);
		if (self::SCOPE_COL) {
			\$stmt->bindValue(':scope', \$node->getScopeIdValue());
		}
		\$stmt->execute();
		\$row = \$stmt->fetch();
		return \$row['level'];
	}
";
	}

	protected function addGetNumberOfChildren(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Gets number of direct children for given node
	 *
	 * @param      $objectClassname \$node	Propel object for src node
	 * @param      PropelPDO \$con	Connection to use.
	 * @return     int			Level for the given node
	 */
	public static function getNumberOfChildren(NodeObject \$node, PropelPDO \$con = null)
	{
		\$children = $peerClassname::retrieveChildren(\$node);
		return count(\$children);
	}
";
	}

	protected function addGetNumberOfDescendants(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Gets number of descendants for given node
	 *
	 * @param      $objectClassname \$node	Propel object for src node
	 * @param      PropelPDO \$con	Connection to use.
	 * @return     int			Level for the given node
	 */
	public static function getNumberOfDescendants(NodeObject \$node, PropelPDO \$con = null)
	{
		\$right = \$node->getRightValue();
		\$left = \$node->getLeftValue();
		\$num = (\$right - \$left - 1) / 2;
		return \$num;
	}
";
	}

	protected function addGetPath(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Returns path to a specific node as an array, useful to create breadcrumbs
	 *
	 * @param      $objectClassname \$node	Propel object of node to create path to
	 * @param      PropelPDO \$con	Connection to use.
	 * @return     array			Array in order of heirarchy
	 */
	public static function getPath(NodeObject \$node, PropelPDO \$con = null)
	{
		\$criteria = new Criteria();
		if (self::SCOPE_COL) {
			\$criteria->add(self::SCOPE_COL, \$node->getScopeIdValue(), Criteria::EQUAL);
		}
		\$criteria->add(self::LEFT_COL, \$node->getLeftValue(), Criteria::LESS_EQUAL);
		\$criteria->add(self::RIGHT_COL, \$node->getRightValue(), Criteria::GREATER_EQUAL);
		\$criteria->addAscendingOrderByColumn(self::LEFT_COL);

		return self::doSelect(\$criteria, \$con);
	}
";
	}

	protected function addIsValid(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Tests if node is valid
	 *
	 * @param      $objectClassname \$node	Propel object for src node
	 * @return     bool
	 */
	public static function isValid(NodeObject \$node = null)
	{
		if (is_object(\$node) && \$node->getRightValue() > \$node->getLeftValue()) {
			return true;
		} else {
			return false;
		}
	}
";
	}

	protected function addIsRoot(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Tests if node is a root
	 *
	 * @param      $objectClassname \$node	Propel object for src node
	 * @return     bool
	 */
	public static function isRoot(NodeObject \$node)
	{
		return (\$node->getLeftValue()==1);
	}
";
	}

	protected function addIsLeaf(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Tests if node is a leaf
	 *
	 * @param      $objectClassname \$node	Propel object for src node
	 * @return     bool
	 */
	public static function isLeaf(NodeObject \$node)
	{
		return ((\$node->getRightValue()-\$node->getLeftValue())==1);
	}
";
	}

	protected function addIsChildOf(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Tests if \$child is a child of \$parent
	 *
	 * @param      $objectClassname \$child	Propel object for node
	 * @param      $objectClassname \$parent	Propel object for node
	 * @return     bool
	 */
	public static function isChildOf(NodeObject \$child, NodeObject \$parent)
	{
		return ((\$child->getLeftValue()>\$parent->getLeftValue()) && (\$child->getRightValue()<\$parent->getRightValue()));
	}
";
	}

	protected function addIsChildOfOrSiblingTo(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Tests if \$node1 is a child of or equal to \$node2
	 *
	 * @deprecated 1.3 - 2007/11/09
	 * @param      $objectClassname \$node1		Propel object for node
	 * @param      $objectClassname \$node2		Propel object for node
	 * @return     bool
	 */
	public static function isChildOfOrSiblingTo(NodeObject \$node1, NodeObject \$node2)
	{
		return ((\$node1->getLeftValue()>=\$node2->getLeftValue()) and (\$node1->getRightValue()<=\$node2->getRightValue()));
	}
";
	}

	protected function addIsEqualTo(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Tests if \$node1 is equal to \$node2
	 *
	 * @param      $objectClassname \$node1		Propel object for node
	 * @param      $objectClassname \$node2		Propel object for node
	 * @return     bool
	 */
	public static function isEqualTo(NodeObject \$node1, NodeObject \$node2)
	{
		\$also = true;
		if (self::SCOPE_COL) {
			\$also = (\$node1->getScopeIdValue() === \$node2->getScopeIdValue());
		}
		return \$node1->getLeftValue() == \$node2->getLeftValue() && \$node1->getRightValue() == \$node2->getRightValue() && \$also;
	}
";
	}

	protected function addHasParent(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Tests if \$node has an ancestor
	 *
	 * @param      $objectClassname \$node		Propel object for node
	 * @param      PropelPDO \$con		Connection to use.
	 * @return     bool
	 */
	public static function hasParent(NodeObject \$node, PropelPDO \$con = null)
	{
		return $peerClassname::isValid($peerClassname::retrieveParent(\$node, \$con));
	}
";
	}

	protected function addHasPrevSibling(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Tests if \$node has prev sibling
	 *
	 * @param      $objectClassname \$node		Propel object for node
	 * @param      PropelPDO \$con		Connection to use.
	 * @return     bool
	 */
	public static function hasPrevSibling(NodeObject \$node, PropelPDO \$con = null)
	{
		return $peerClassname::isValid($peerClassname::retrievePrevSibling(\$node, \$con));
	}
";
	}

	protected function addHasNextSibling(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Tests if \$node has next sibling
	 *
	 * @param      $objectClassname \$node		Propel object for node
	 * @param      PropelPDO \$con		Connection to use.
	 * @return     bool
	 */
	public static function hasNextSibling(NodeObject \$node, PropelPDO \$con = null)
	{
		return $peerClassname::isValid($peerClassname::retrieveNextSibling(\$node, \$con));
	}
";
	}

	protected function addHasChildren(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Tests if \$node has children
	 *
	 * @param      $objectClassname \$node		Propel object for node
	 * @return     bool
	 */
	public static function hasChildren(NodeObject \$node)
	{
		return ((\$node->getRightValue()-\$node->getLeftValue())>1);
	}
";
	}

	protected function addDeleteDescendants(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Deletes \$node and all of its descendants
	 *
	 * @param      $objectClassname \$node		Propel object for source node
	 * @param      PropelPDO \$con		Connection to use.
	 */
	public static function deleteDescendants(NodeObject \$node, PropelPDO \$con = null)
	{
		\$left = \$node->getLeftValue();
		\$right = \$node->getRightValue();

		\$c = new Criteria($peerClassname::DATABASE_NAME);
		\$c1 = \$c->getNewCriterion(self::LEFT_COL, \$left, Criteria::GREATER_THAN);
		\$c2 = \$c->getNewCriterion(self::RIGHT_COL, \$right, Criteria::LESS_THAN);

		\$c1->addAnd(\$c2);

		\$c->add(\$c1);
		if (self::SCOPE_COL) {
			\$c->add(self::SCOPE_COL, \$node->getScopeIdValue(), Criteria::EQUAL);
		}
		\$c->addAscendingOrderByColumn(self::RIGHT_COL);

		\$result = $peerClassname::doDelete(\$c, \$con);

		self::shiftRLValues(\$right + 1, \$left - \$right -1, \$con, \$node->getScopeIdValue());

		return \$result;
	}
";
	}

	protected function addGetNode(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Returns a node given its primary key or the node itself
	 *
	 * @param      int/$objectClassname \$node	Primary key/instance of required node
	 * @param      PropelPDO \$con		Connection to use.
	 * @return     object		Propel object for model
	 */
	public static function getNode(\$node, PropelPDO \$con = null)
	{
		if (is_object(\$node)) {
			return \$node;
		} else {
			\$object = $peerClassname::retrieveByPK(\$node, \$con);
			\$rtn = is_object(\$object) ? \$object : false;
			return \$rtn;
		}
	}
";
	}

	protected function addHydrateDescendants(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$table = $this->getTable();
		$script .= "
	/**
	 * Hydrate recursively the descendants of the given node
	 * @param      $objectClassname \$node	Propel object for src node
	 * @param      PDOStatement \$stmt	Executed PDOStatement
	 */
	protected static function hydrateDescendants(NodeObject \$node, PDOStatement \$stmt)
	{
		\$descendants = array();
		\$children = array();
		\$prevSibling = null;
";

		if (!$table->getChildrenColumn()) {
			$script .= "
		// set the class once to avoid overhead in the loop
		\$cls = $peerClassname::getOMClass();
		\$cls = substr('.'.\$cls, strrpos('.'.\$cls, '.') + 1);
";
		}

		$script .= "
		while (\$row = \$stmt->fetch(PDO::FETCH_NUM)) {
			\$key = ".$peerClassname."::getPrimaryKeyHashFromRow(\$row, 0);
			if (null === (\$child = ".$peerClassname."::getInstanceFromPool(\$key))) {";

		if ($table->getChildrenColumn()) {
			$script .= "
				// class must be set each time from the record row
				\$cls = ".$peerClassname."::getOMClass(\$row, 0);
				\$cls = substr('.'.\$cls, strrpos('.'.\$cls, '.') + 1);
";
		}

		$script .= "
				" . $this->buildObjectInstanceCreationCode('$child', '$cls') . "
				\$child->hydrate(\$row);
			}

			\$child->setLevel(\$node->getLevel() + 1);
			\$child->setParentNode(\$node);
			if (!empty(\$prevSibling)) {
				\$child->setPrevSibling(\$prevSibling);
				\$prevSibling->setNextSibling(\$child);
			}

			\$descendants[] = \$child;

			if (\$child->hasChildren()) {
				\$descendants = array_merge(\$descendants, $peerClassname::hydrateDescendants(\$child, \$stmt));
			} else {
				\$child->setChildren(array());
			}

			\$children[] = \$child;
			\$prevSibling = \$child;

			$peerClassname::addInstanceToPool(\$child);
			if (\$child->getRightValue() + 1 == \$node->getRightValue()) {
				\$child->setNextSibling(null);
				break;
			}
		}
		\$node->setChildren(\$children);
		return \$descendants;
	}
";
	}

	protected function addHydrateChildren(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$table = $this->getTable();
		$script .= "
	/**
	 * Hydrate the children of the given node
	 * @param      $objectClassname \$node Propel object for src node
	 * @param      PDOStatement \$stmt Executed PDOStatement
	 */
	protected static function hydrateChildren(NodeObject \$node, PDOStatement \$stmt)
	{
		\$children = array();
		\$prevRight = 0;
";

		if (!$table->getChildrenColumn()) {
			$script .= "
		// set the class once to avoid overhead in the loop
		\$cls = $peerClassname::getOMClass();
		\$cls = substr('.'.\$cls, strrpos('.'.\$cls, '.') + 1);
";
		}

		$script .= "
		while (\$row = \$stmt->fetch(PDO::FETCH_NUM)) {
			\$key = ".$peerClassname."::getPrimaryKeyHashFromRow(\$row, 0);
			if (null === (\$child = ".$peerClassname."::getInstanceFromPool(\$key))) {";
			
		if ($table->getChildrenColumn()) {
			$script .= "
				// class must be set each time from the record row
				\$cls = ".$peerClassname."::getOMClass(\$row, 0);
				\$cls = substr('.'.\$cls, strrpos('.'.\$cls, '.') + 1);
";
		}

		$script .= "
				" . $this->buildObjectInstanceCreationCode('$child', '$cls') . "
				\$child->hydrate(\$row);
			}

			\$child->setLevel(\$node->getLevel() + 1);

			if (\$child->getRightValue() > \$prevRight) {
				\$children[] = \$child;
				\$prevRight = \$child->getRightValue();
			}

			if (\$child->getRightValue() + 1 == \$node->getRightValue()) {
				break;
			}
		}
		\$node->setChildren(\$children);
		return \$children;
	}
";
	}

	/**
	 * @deprecated 1.3 - 2008/03/11
	 * Won't be fixed, defect by design
	 * Never trust it
	 */
	protected function addShiftRParent(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Adds '\$delta' to all parent R values.
	 * '\$delta' can also be negative.
	 *
	 * @deprecated 1.3 - 2008/03/11
	 * @param      $objectClassname \$node	Propel object for parent node
	 * @param      int \$delta	Value to be shifted by, can be negative
	 * @param      PropelPDO \$con		Connection to use.
	 */
	protected static function shiftRParent(NodeObject \$node, \$delta, PropelPDO \$con = null)
	{
		if (\$node->hasParent(\$con)) {
			\$parent = \$node->retrieveParent();
			self::shiftRParent(\$parent, \$delta, \$con);
		}
		\$node->setRightValue(\$node->getRightValue() + \$delta);
	}
";
	}

	protected function addUpdateLoadedNode(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$table = $this->getTable();

		$script .= "
	/**
	 * Reload all already loaded nodes to sync them with updated db
	 *
	 * @param      $objectClassname \$node	Propel object for parent node
	 * @param      int \$delta	Value to be shifted by, can be negative
	 * @param      PropelPDO \$con		Connection to use.
	 */
	protected static function updateLoadedNode(NodeObject \$node, \$delta, PropelPDO \$con = null)
	{
		if (Propel::isInstancePoolingEnabled()) {
			\$keys = array();
			foreach (self::\$instances as \$obj) {
				\$keys[] = \$obj->getPrimaryKey();
			}

			if (!empty(\$keys)) {
				// We don't need to alter the object instance pool; we're just modifying these ones
				// already in the pool.
				\$criteria = new Criteria(self::DATABASE_NAME);";
		if (count($table->getPrimaryKey()) === 1) {
			$pkey = $table->getPrimaryKey();
			$col = array_shift($pkey);
			$script .= "
				\$criteria->add(".$this->getColumnConstant($col).", \$keys, Criteria::IN);
";
		} else {
			$fields = array();
			foreach ($table->getPrimaryKey() as $k => $col) {
				$fields[] = $this->getColumnConstant($col);
			};
			$script .= "

				// Loop on each instances in pool
				foreach (\$keys as \$values) {
				  // Create initial Criterion
					\$cton = \$criteria->getNewCriterion(" . $fields[0] . ", \$values[0]);";
			unset($fields[0]);
			foreach ($fields as $k => $col) {
				$script .= "

					// Create next criterion
					\$nextcton = \$criteria->getNewCriterion(" . $col . ", \$values[$k]);
					// And merge it with the first
					\$cton->addAnd(\$nextcton);";
			}
			$script .= "

					// Add final Criterion to Criteria
					\$criteria->addOr(\$cton);
				}";
			}

			$script .= "
				\$stmt = $peerClassname::doSelectStmt(\$criteria, \$con);
				while (\$row = \$stmt->fetch(PDO::FETCH_NUM)) {
					\$key = $peerClassname::getPrimaryKeyHashFromRow(\$row, 0);
					if (null !== (\$object = $peerClassname::getInstanceFromPool(\$key))) {";
			$n = 0;
			foreach ($table->getColumns() as $col) {
				if ($col->isNestedSetLeftKey()) {
					$script .= "
						\$object->setLeftValue(\$row[$n]);";
				} else if ($col->isNestedSetRightKey()) {
					$script .= "
						\$object->setRightValue(\$row[$n]);";
				}
				$n++;
			}
			$script .= "
					}
				}
				\$stmt->closeCursor();
			}
		}
	}
";
	}

	protected function addUpdateDBNode(&$script)
	{
		$objectClassname = $this->getStubObjectBuilder()->getClassname();
		$script .= "
	/**
	 * Move \$node and its children to location \$destLeft and updates rest of tree
	 *
	 * @param      $objectClassname \$node Propel object for node to update
	 * @param      int	\$destLeft Destination left value
	 * @param      PropelPDO \$con		Connection to use.
	 */
	protected static function updateDBNode(NodeObject \$node, \$destLeft, PropelPDO \$con = null)
	{
		\$left = \$node->getLeftValue();
		\$right = \$node->getRightValue();

		\$treeSize = \$right - \$left +1;

		self::shiftRLValues(\$destLeft, \$treeSize, \$con, \$node->getScopeIdValue());

		if (\$left >= \$destLeft) { // src was shifted too?
			\$left += \$treeSize;
			\$right += \$treeSize;
		}

		// now there's enough room next to target to move the subtree
		self::shiftRLRange(\$left, \$right, \$destLeft - \$left, \$con, \$node->getScopeIdValue());

		// correct values after source
		self::shiftRLValues(\$right + 1, -\$treeSize, \$con, \$node->getScopeIdValue());
	}
";
	}

	protected function addShiftRLValues(&$script)
	{
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Adds '\$delta' to all L and R values that are >= '\$first'. '\$delta' can also be negative.
	 *
	 * @param      int \$first		First node to be shifted
	 * @param      int \$delta		Value to be shifted by, can be negative
	 * @param      PropelPDO \$con		Connection to use.
	 */
	protected static function shiftRLValues(\$first, \$delta, PropelPDO \$con = null, \$scopeId = null)
	{
		if (\$con === null) {
			\$con = Propel::getConnection($peerClassname::DATABASE_NAME, Propel::CONNECTION_WRITE);
		}

		\$leftUpdateCol = substr(self::LEFT_COL, strrpos(self::LEFT_COL, '.') + 1);
		\$rightUpdateCol = substr(self::RIGHT_COL, strrpos(self::RIGHT_COL, '.') + 1);

		// Shift left column values
		\$whereCriteria = new Criteria($peerClassname::DATABASE_NAME);
		\$criterion = \$whereCriteria->getNewCriterion(
			self::LEFT_COL,
			\$first,
			Criteria::GREATER_EQUAL);

		if (self::SCOPE_COL) {
			\$criterion->addAnd(
				\$whereCriteria->getNewCriterion(
					self::SCOPE_COL,
					\$scopeId,
					Criteria::EQUAL));
		}
		\$whereCriteria->add(\$criterion);

		\$valuesCriteria = new Criteria($peerClassname::DATABASE_NAME);
		\$valuesCriteria->add(
			self::LEFT_COL,
			array('raw' => \$leftUpdateCol . ' + ?', 'value' => \$delta),
			Criteria::CUSTOM_EQUAL);

		{$this->basePeerClassname}::doUpdate(\$whereCriteria, \$valuesCriteria, \$con);

		// Shift right column values
		\$whereCriteria = new Criteria($peerClassname::DATABASE_NAME);
		\$criterion = \$whereCriteria->getNewCriterion(
			self::RIGHT_COL,
			\$first,
			Criteria::GREATER_EQUAL);

		if (self::SCOPE_COL) {
			\$criterion->addAnd(
				\$whereCriteria->getNewCriterion(
					self::SCOPE_COL,
					\$scopeId,
					Criteria::EQUAL));
		}
		\$whereCriteria->add(\$criterion);

		\$valuesCriteria = new Criteria($peerClassname::DATABASE_NAME);
		\$valuesCriteria->add(
		  self::RIGHT_COL,
			array('raw' => \$rightUpdateCol . ' + ?', 'value' => \$delta),
			Criteria::CUSTOM_EQUAL);

		{$this->basePeerClassname}::doUpdate(\$whereCriteria, \$valuesCriteria, \$con);
	}
";
	}

	protected function addShiftRLRange(&$script)
	{
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$script .= "
	/**
	 * Adds '\$delta' to all L and R values that are >= '\$first' and <= '\$last'.
	 * '\$delta' can also be negative.
	 *
	 * @param      int \$first	First node to be shifted (L value)
	 * @param      int \$last	Last node to be shifted (L value)
	 * @param      int \$delta	Value to be shifted by, can be negative
	 * @param      PropelPDO \$con		Connection to use.
	 * @return     array 		Shifted L and R values
	 */
	protected static function shiftRLRange(\$first, \$last, \$delta, PropelPDO \$con = null, \$scopeId = null)
	{
		if (\$con === null) {
			\$con = Propel::getConnection($peerClassname::DATABASE_NAME, Propel::CONNECTION_WRITE);
		}

		\$leftUpdateCol = substr(self::LEFT_COL, strrpos(self::LEFT_COL, '.') + 1);
		\$rightUpdateCol = substr(self::RIGHT_COL, strrpos(self::RIGHT_COL, '.') + 1);

		// Shift left column values
		\$whereCriteria = new Criteria($peerClassname::DATABASE_NAME);
		\$criterion = \$whereCriteria->getNewCriterion(self::LEFT_COL, \$first, Criteria::GREATER_EQUAL);
		\$criterion->addAnd(\$whereCriteria->getNewCriterion(self::LEFT_COL, \$last, Criteria::LESS_EQUAL));
		if (self::SCOPE_COL) {
			\$criterion->addAnd(\$whereCriteria->getNewCriterion(self::SCOPE_COL, \$scopeId, Criteria::EQUAL));
		}
		\$whereCriteria->add(\$criterion);

		\$valuesCriteria = new Criteria($peerClassname::DATABASE_NAME);
		\$valuesCriteria->add(
			self::LEFT_COL,
			array('raw' => \$leftUpdateCol . ' + ?', 'value' => \$delta),
			Criteria::CUSTOM_EQUAL);

		{$this->basePeerClassname}::doUpdate(\$whereCriteria, \$valuesCriteria, \$con);

		// Shift right column values
		\$whereCriteria = new Criteria($peerClassname::DATABASE_NAME);
		\$criterion = \$whereCriteria->getNewCriterion(self::RIGHT_COL, \$first, Criteria::GREATER_EQUAL);
		\$criterion->addAnd(\$whereCriteria->getNewCriterion(self::RIGHT_COL, \$last, Criteria::LESS_EQUAL));
		if (self::SCOPE_COL) {
			\$criterion->addAnd(\$whereCriteria->getNewCriterion(self::SCOPE_COL, \$scopeId, Criteria::EQUAL));
		}
		\$whereCriteria->add(\$criterion);

		\$valuesCriteria = new Criteria($peerClassname::DATABASE_NAME);
		\$valuesCriteria->add(
			self::RIGHT_COL,
			array('raw' => \$rightUpdateCol . ' + ?', 'value' => \$delta),
			Criteria::CUSTOM_EQUAL);

		{$this->basePeerClassname}::doUpdate(\$whereCriteria, \$valuesCriteria, \$con);

		return array('left' => \$first + \$delta, 'right' => \$last + \$delta);
	}
";
	}

} // PHP5NestedSetPeerBuilder
