<?php

/*
 *  $Id: PHP5NodeBuilder.php 536 2007-01-10 14:30:38Z 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/ObjectBuilder.php';

/**
 * Generates a PHP5 tree node Object class for user object model (OM).
 *
 * This class produces the base tree node 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     Hans Lellelid <hans@xmpl.org>
 * @package    propel.engine.builder.om.php5
 */
class PHP5NodeBuilder extends ObjectBuilder {

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

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

	/**
	 * Adds the include() statements for files that this class depends on or utilizes.
	 * @param      string &$script The script will be modified in this method.
	 */
	protected function addIncludes(&$script)
	{
		$script .= "
require_once '".$this->getStubNodePeerBuilder()->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 class that represents a row from the '$tableName' table.
 *
 * $tableDesc
 *";
		if ($this->getBuildProperty('addTimeStamp')) {
			$now = strftime('%c');
			$script .= "
 * This class was autogenerated by Propel on:
 *
 * $now
 *";
		}
		$script .= "
 * @package    ".$this->getPackage()."
 */
abstract class ".$this->getClassname()." implements IteratorAggregate {
";
	}

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

		$this->addAttributes($script);

		$this->addConstructor($script);

		$this->addCallOverload($script);
		$this->addSetIteratorOptions($script);
		$this->addGetIterator($script);

		$this->addGetNodeObj($script);
		$this->addGetNodePath($script);
		$this->addGetNodeIndex($script);
		$this->addGetNodeLevel($script);

		$this->addHasChildNode($script);
		$this->addGetChildNodeAt($script);
		$this->addGetFirstChildNode($script);
		$this->addGetLastChildNode($script);
		$this->addGetSiblingNode($script);

		$this->addGetParentNode($script);
		$this->addGetAncestors($script);
		$this->addIsRootNode($script);

		$this->addSetNew($script);
		$this->addSetDeleted($script);
		$this->addAddChildNode($script);
		$this->addMoveChildNode($script);
		$this->addSave($script);

		$this->addDelete($script);
		$this->addEquals($script);

		$this->addAttachParentNode($script);
		$this->addAttachChildNode($script);
		$this->addDetachParentNode($script);
		$this->addDetachChildNode($script);
		$this->addShiftChildNodes($script);
		$this->addInsertNewChildNode($script);

		$this->addAdjustStatus($script);
		$this->addAdjustNodePath($script);

	}

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


	/**
	 * Adds class attributes.
	 * @param      string &$script The script will be modified in this method.
	 */
	protected function addAttributes(&$script)
	{
		$script .= "
	/**
	 * @var        ".$this->getStubObjectBuilder()->getClassname()." object wrapped by this node.
	 */
	protected \$obj = null;

	/**
	 * The parent node for this node.
	 * @var        ".$this->getStubNodeBuilder()->getClassname()."
	 */
	protected \$parentNode = null;

	/**
	 * Array of child nodes for this node. Nodes indexes are one-based.
	 * @var        array
	 */
	protected \$childNodes = array();
";
	}

	/**
	 * Adds the constructor.
	 * @param      string &$script The script will be modified in this method.
	 */
	protected function addConstructor(&$script)
	{
		$script .= "
	/**
	 * Constructor.
	 *
	 * @param      ".$this->getStubObjectBuilder()->getClassname()." \$obj Object wrapped by this node.
	 */
	public function __construct(\$obj = null)
	{
		if (\$obj !== null) {
			\$this->obj = \$obj;
		} else {
			\$setNodePath = 'set' . ".$this->getStubNodePeerBuilder()->getClassname()."::NPATH_PHPNAME;
			\$this->obj = new ".$this->getStubObjectBuilder()->getClassname()."();
			\$this->obj->\$setNodePath('0');
		}
	}
";
	}



	protected function addCallOverload(&$script)
	{
		$script .= "
	/**
	 * Convenience overload for wrapped object methods.
	 *
	 * @param      string Method name to call on wrapped object.
	 * @param      mixed Parameter accepted by wrapped object set method.
	 * @return     mixed Return value of wrapped object method.
	 * @throws     PropelException Fails if method is not defined for wrapped object.
	 */
	public function __call(\$name, \$parms)
	{
		if (method_exists(\$this->obj, \$name))
			return call_user_func_array(array(\$this->obj, \$name), \$parms);
		else
			throw new PropelException('get method not defined: \$name');
	}
";
	}

	protected function addSetIteratorOptions(&$script)
	{
		$script .= "

	/**
	 * Sets the default options for iterators created from this object.
	 * The options are specified in map format. The following options
	 * are supported by all iterators. Some iterators may support other
	 * options:
	 *
	 *   \"querydb\" - True if nodes should be retrieved from database.
	 *   \"con\" - Connection to use if retrieving from database.
	 *
	 * @param      string Type of iterator to use (\"pre\", \"post\", \"level\").
	 * @param      array Map of option name => value.
	 * @return     void
	 * @todo       Implement other iterator types (i.e. post-order, level, etc.)
	 */
	public function setIteratorOptions(\$type, \$opts)
	{
		\$this->itType = \$type;
		\$this->itOpts = \$opts;
	}
";
	}

	protected function addGetIterator(&$script)
	{
		$script .= "
	/**
	 * Returns a pre-order iterator for this node and its children.
	 *
	 * @param      string Type of iterator to use (\"pre\", \"post\", \"level\")
	 * @param      array Map of option name => value.
	 * @return     NodeIterator
	 */
	public function getIterator(\$type = null, \$opts = null)
	{
		if (\$type === null)
			\$type = (isset(\$this->itType) ? \$this->itType : 'Pre');

		if (\$opts === null)
			\$opts = (isset(\$this->itOpts) ? \$this->itOpts : array());

		\$itclass = ucfirst(strtolower(\$type)) . 'OrderNodeIterator';

		require_once('propel/om/' . \$itclass . '.php');
		return new \$itclass(\$this, \$opts);
	}
";
	}

	protected function addGetNodeObj(&$script)
	{
		$script .= "
	/**
	 * Returns the object wrapped by this class.
	 * @return     ".$this->getStubObjectBuilder()->getClassname()."
	 */
	public function getNodeObj()
	{
		return \$this->obj;
	}
";
	}

	protected function addGetNodePath(&$script)
	{
		$script .= "
	/**
	 * Convenience method for retrieving nodepath.
	 * @return     string
	 */
	public function getNodePath()
	{
		\$getNodePath = 'get' . ".$this->getStubNodePeerBuilder()->getClassname()."::NPATH_PHPNAME;
		return \$this->obj->\$getNodePath();
	}
";
	}

	protected function addGetNodeIndex(&$script)
	{
		$script .= "
	/**
	 * Returns one-based node index among siblings.
	 * @return     int
	 */
	public function getNodeIndex()
	{
		\$npath =& \$this->getNodePath();
		\$sep = strrpos(\$npath, ".$this->getStubNodePeerBuilder()->getClassname()."::NPATH_SEP);
		return (int) (\$sep !== false ? substr(\$npath, \$sep+1) : \$npath);
	}
";
	}

	protected function addGetNodeLevel(&$script)
	{
		$script .= "
	/**
	 * Returns one-based node level within tree (root node is level 1).
	 * @return     int
	 */
	public function getNodeLevel()
	{
		return (substr_count(\$this->getNodePath(), ".$this->getStubNodePeerBuilder()->getClassname()."::NPATH_SEP) + 1);
	}
";
	}

	protected function addHasChildNode(&$script)
	{
		$script .= "
	/**
	 * Returns true if specified node is a child of this node. If recurse is
	 * true, checks if specified node is a descendant of this node.
	 *
	 * @param      ".$this->getStubNodeBuilder()->getClassname()." Node to look for.
	 * @param      boolean True if strict comparison should be used.
	 * @param      boolean True if all descendants should be checked.
	 * @return     boolean
	 */
	public function hasChildNode(\$node, \$strict = false, \$recurse = false)
	{
		foreach (\$this->childNodes as \$childNode)
		{
			if (\$childNode->equals(\$node, \$strict))
				return true;

			if (\$recurse && \$childNode->hasChildNode(\$node, \$recurse))
				return true;
		}

		return false;
	}
";
	}

	protected function addGetChildNodeAt(&$script)
	{
		$script .= "
	/**
	 * Returns child node at one-based index. Retrieves from database if not
	 * loaded yet.
	 *
	 * @param      int One-based child node index.
	 * @param      boolean True if child should be retrieved from database.
	 * @param      Connection Connection to use if retrieving from database.
	 * @return     ".$this->getStubNodeBuilder()->getClassname()."
	 */
	public function getChildNodeAt(\$i, \$querydb = false, \$con = null)
	{
		if (\$querydb &&
			!\$this->obj->isNew() &&
			!\$this->obj->isDeleted() &&
			!isset(\$this->childNodes[\$i]))
		{
			\$criteria = new Criteria(".$this->getStubPeerBuilder()->getClassname()."::DATABASE_NAME);
			\$criteria->add(".$this->getStubNodePeerBuilder()->getClassname()."::NPATH_COLNAME, \$this->getNodePath() . ".$this->getStubNodePeerBuilder()->getClassname()."::NPATH_SEP . \$i, Criteria::EQUAL);

			if (\$childObj = ".$this->getStubPeerBuilder()->getClassname()."::doSelectOne(\$criteria, \$con))
				\$this->attachChildNode(new ".$this->getStubNodeBuilder()->getClassname()."(\$childObj));
		}

		return (isset(\$this->childNodes[\$i]) ? \$this->childNodes[\$i] : null);
	}
";
	}

	protected function addGetFirstChildNode(&$script)
	{
		$script .= "
	/**
	 * Returns first child node (if any). Retrieves from database if not loaded yet.
	 *
	 * @param      boolean True if child should be retrieved from database.
	 * @param      Connection Connection to use if retrieving from database.
	 * @return     ".$this->getStubNodeBuilder()->getClassname()."
	 */
	public function getFirstChildNode(\$querydb = false, \$con = null)
	{
		return \$this->getChildNodeAt(1, \$querydb, \$con);
	}
";
	}

	protected function addGetLastChildNode(&$script)
	{
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$nodePeerClassname = $this->getStubNodePeerBuilder()->getClassname();

		$script .= "
	/**
	 * Returns last child node (if any).
	 *
	 * @param      boolean True if child should be retrieved from database.
	 * @param      Connection Connection to use if retrieving from database.
	 */
	public function getLastChildNode(\$querydb = false, \$con = null)
	{
		\$lastNode = null;

		if (\$this->obj->isNew() || \$this->obj->isDeleted())
		{
			end(\$this->childNodes);
			\$lastNode = (count(\$this->childNodes) ? current(\$this->childNodes) : null);
		}
		else if (\$querydb)
		{
			\$db = Propel::getDb($peerClassname::DATABASE_NAME);
			\$criteria = new Criteria($peerClassname::DATABASE_NAME);
			\$criteria->add($nodePeerClassname::NPATH_COLNAME, \$this->getNodePath() . $nodePeerClassname::NPATH_SEP . '%', Criteria::LIKE);
			\$criteria->addAnd($nodePeerClassname::NPATH_COLNAME, \$this->getNodePath() . $nodePeerClassname::NPATH_SEP . '%' . $nodePeerClassname::NPATH_SEP . '%', Criteria::NOT_LIKE);
			\$criteria->addAsColumn('npathlen', \$db->strLength($nodePeerClassname::NPATH_COLNAME));
			\$criteria->addDescendingOrderByColumn('npathlen');
			\$criteria->addDescendingOrderByColumn($nodePeerClassname::NPATH_COLNAME);

			\$lastObj = $peerClassname::doSelectOne(\$criteria, \$con);

			if (\$lastObj !== null)
			{
				\$lastNode = new ".$this->getStubNodeBuilder()->getClassname()."(\$lastObj);

				end(\$this->childNodes);
				\$endNode = (count(\$this->childNodes) ? current(\$this->childNodes) : null);

				if (\$endNode)
				{
					if (\$endNode->getNodePath() > \$lastNode->getNodePath())
						throw new PropelException('Cached child node inconsistent with database.');
					else if (\$endNode->getNodePath() == \$lastNode->getNodePath())
						\$lastNode = \$endNode;
					else
						\$this->attachChildNode(\$lastNode);
				}
				else
				{
					\$this->attachChildNode(\$lastNode);
				}
			}
		}

		return \$lastNode;
	}
";
	}

	protected function addGetSiblingNode(&$script)
	{
		$script .= "
	/**
	 * Returns next (or previous) sibling node or null. Retrieves from database if
	 * not loaded yet.
	 *
	 * @param      boolean True if previous sibling should be returned.
	 * @param      boolean True if sibling should be retrieved from database.
	 * @param      Connection Connection to use if retrieving from database.
	 * @return     ".$this->getStubNodeBuilder()->getClassname()."
	 */
	public function getSiblingNode(\$prev = false, \$querydb = false, \$con = null)
	{
		\$nidx = \$this->getNodeIndex();

		if (\$this->isRootNode())
		{
			return null;
		}
		else if (\$prev)
		{
			if (\$nidx > 1 && (\$parentNode = \$this->getParentNode(\$querydb, \$con)))
				return \$parentNode->getChildNodeAt(\$nidx-1, \$querydb, \$con);
			else
				return null;
		}
		else
		{
			if (\$parentNode = \$this->getParentNode(\$querydb, \$con))
				return \$parentNode->getChildNodeAt(\$nidx+1, \$querydb, \$con);
			else
				return null;
		}
	}
";
	}

	protected function addGetParentNode(&$script)
	{
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$nodePeerClassname = $this->getStubNodePeerBuilder()->getClassname();

		$script .= "
	/**
	 * Returns parent node. Loads from database if not cached yet.
	 *
	 * @param      boolean True if parent should be retrieved from database.
	 * @param      Connection Connection to use if retrieving from database.
	 * @return     ".$this->getStubNodeBuilder()->getClassname()."
	 */
	public function getParentNode(\$querydb = true, \$con = null)
	{
		if (\$querydb &&
			\$this->parentNode === null &&
			!\$this->isRootNode() &&
			!\$this->obj->isNew() &&
			!\$this->obj->isDeleted())
		{
			\$npath =& \$this->getNodePath();
			\$sep = strrpos(\$npath, $nodePeerClassname::NPATH_SEP);
			\$ppath = substr(\$npath, 0, \$sep);

			\$criteria = new Criteria($peerClassname::DATABASE_NAME);
			\$criteria->add($nodePeerClassname::NPATH_COLNAME, \$ppath, Criteria::EQUAL);

			if (\$parentObj = $peerClassname::doSelectOne(\$criteria, \$con))
			{
				\$parentNode = new ".$this->getStubNodeBuilder()->getClassname()."(\$parentObj);
				\$parentNode->attachChildNode(\$this);
			}
		}

		return \$this->parentNode;
	}
";
	}

	protected function addGetAncestors(&$script)
	{
		$script .= "
	/**
	 * Returns an array of all ancestor nodes, starting with the root node
	 * first.
	 *
	 * @param      boolean True if ancestors should be retrieved from database.
	 * @param      Connection Connection to use if retrieving from database.
	 * @return     array
	 */
	public function getAncestors(\$querydb = false, \$con = null)
	{
		\$ancestors = array();
		\$parentNode = \$this;

		while (\$parentNode = \$parentNode->getParentNode(\$querydb, \$con))
			array_unshift(\$ancestors, \$parentNode);

		return \$ancestors;
	}
";
	}

	protected function addIsRootNode(&$script)
	{
		$script .= "
	/**
	 * Returns true if node is the root node of the tree.
	 * @return     boolean
	 */
	public function isRootNode()
	{
		return (\$this->getNodePath() === '1');
	}
";
	}

	protected function addSetNew(&$script)
	{
		$script .= "
	/**
	 * Changes the state of the object and its descendants to 'new'.
	 * Also changes the node path to '0' to indicate that it is not a
	 * stored node.
	 *
	 * @param      boolean
	 * @return     void
	 */
	public function setNew(\$b)
	{
		\$this->adjustStatus('new', \$b);
		\$this->adjustNodePath(\$this->getNodePath(), '0');
	}
";
	}

	protected function addSetDeleted(&$script)
	{
		$script .= "
	/**
	 * Changes the state of the object and its descendants to 'deleted'.
	 *
	 * @param      boolean
	 * @return     void
	 */
	public function setDeleted(\$b)
	{
		\$this->adjustStatus('deleted', \$b);
	}
";
	}

	protected function addAddChildNode(&$script)
	{
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$nodePeerClassname = $this->getStubNodePeerBuilder()->getClassname();

		$script .= "
	/**
	 * Adds the specified node (and its children) as a child to this node. If a
	 * valid \$beforeNode is specified, the node will be inserted in front of
	 * \$beforeNode. If \$beforeNode is not specified the node will be appended to
	 * the end of the child nodes.
	 *
	 * @param      ".$this->getStubNodeBuilder()->getClassname()." Node to add.
	 * @param      ".$this->getStubNodeBuilder()->getClassname()." Node to insert before.
	 * @param      Connection Connection to use.
	 */
	public function addChildNode(\$node, \$beforeNode = null, \$con = null)
	{
		if (\$this->obj->isNew() && !\$node->obj->isNew())
			throw new PropelException('Cannot add stored nodes to a new node.');

		if (\$this->obj->isDeleted() || \$node->obj->isDeleted())
			throw new PropelException('Cannot add children in a deleted state.');

		if (\$this->hasChildNode(\$node))
			throw new PropelException('Node is already a child of this node.');

		if (\$beforeNode && !\$this->hasChildNode(\$beforeNode))
			throw new PropelException('Invalid beforeNode.');

		if (\$con === null)
			\$con = Propel::getConnection($peerClassname::DATABASE_NAME);

		try {

			if (!\$this->obj->isNew()) \$con->begin();

			if (\$beforeNode)
			{
				// Inserting before a node.
				\$childIdx = \$beforeNode->getNodeIndex();
				\$this->shiftChildNodes(1, \$beforeNode->getNodeIndex(), \$con);
			}
			else
			{
				// Appending child node.
				if (\$lastNode = \$this->getLastChildNode(true, \$con))
					\$childIdx = \$lastNode->getNodeIndex()+1;
				else
					\$childIdx = 1;
			}

			// Add the child (and its children) at the specified index.

			if (!\$this->obj->isNew() && \$node->obj->isNew())
			{
				\$this->insertNewChildNode(\$node, \$childIdx, \$con);
			}
			else
			{
				// \$this->isNew() && \$node->isNew() ||
				// !\$this->isNew() && !node->isNew()

				\$srcPath = \$node->getNodePath();
				\$dstPath = \$this->getNodePath() . $nodePeerClassname::NPATH_SEP . \$childIdx;

				if (!\$node->obj->isNew())
				{
					$nodePeerClassname::moveNodeSubTree(\$srcPath, \$dstPath, \$con);
					\$parentNode = \$node->getParentNode(true, \$con);
				}
				else
				{
					\$parentNode = \$node->getParentNode();
				}

				if (\$parentNode)
				{
					\$parentNode->detachChildNode(\$node);
					\$parentNode->shiftChildNodes(-1, \$node->getNodeIndex()+1, \$con);
				}

				\$node->adjustNodePath(\$srcPath, \$dstPath);
			}

			if (!\$this->obj->isNew()) \$con->commit();

			\$this->attachChildNode(\$node);

		} catch (SQLException \$e) {
			if (!\$this->obj->isNew()) \$con->rollback();
			throw new PropelException(\$e);
		}
	}
";
	}

	protected function addMoveChildNode(&$script)
	{
		$script .= "
	/**
	 * Moves the specified child node in the specified direction.
	 *
	 * @param      ".$this->getStubNodeBuilder()->getClassname()." Node to move.
	 * @param      int Number of spaces to move among siblings (may be negative).
	 * @param      Connection Connection to use.
	 * @throws     PropelException
	 */
	public function moveChildNode(\$node, \$direction, \$con = null)
	{
		throw new PropelException('moveChildNode() not implemented yet.');
	}
";
	}


	protected function addSave(&$script)
	{

		$nodePeerClassname = $this->getStubNodePeerBuilder()->getClassname();
		$script .= "
	/**
	 * Saves modified object data to the datastore.
	 *
	 * @param      boolean If true, descendants will be saved as well.
	 * @param      Connection Connection to use.
	 */
	public function save(\$recurse = false, \$con = null)
	{
		if (\$this->obj->isDeleted())
			throw new PropelException('Cannot save deleted node.');

		if (substr(\$this->getNodePath(), 0, 1) == '0')
			throw new PropelException('Cannot save unattached node.');

		if (\$this->obj->isColumnModified($nodePeerClassname::NPATH_COLNAME))
			throw new PropelException('Cannot save manually modified node path.');

		\$this->obj->save(\$con);

		if (\$recurse)
		{
			foreach (\$this->childNodes as \$childNode)
				\$childNode->save(\$recurse, \$con);
		}
	}
";
	}


	protected function addDelete(&$script)
	{
		$nodePeerClassname = $this->getStubNodePeerBuilder()->getClassname();
		$script .= "
	/**
	 * Removes this object and all descendants from datastore.
	 *
	 * @param      Connection Connection to use.
	 * @return     void
	 * @throws     PropelException
	 */
	public function delete(\$con = null)
	{
		if (\$this->obj->isDeleted())
			throw new PropelException('This node has already been deleted.');

		if (!\$this->obj->isNew())
		{
			$nodePeerClassname::deleteNodeSubTree(\$this->getNodePath(), \$con);
		}

		if (\$parentNode = \$this->getParentNode(true, \$con))
		{
			\$parentNode->detachChildNode(\$this);
			\$parentNode->shiftChildNodes(-1, \$this->getNodeIndex()+1, \$con);
		}

		\$this->setDeleted(true);
	}
";
	}

	protected function addEquals(&$script)
	{
		$nodeClassname = $this->getStubNodeBuilder()->getClassname();
		$script .= "
	/**
	 * Compares the object wrapped by this node with that of another node. Use
	 * this instead of equality operators to prevent recursive dependency
	 * errors.
	 *
	 * @param      $nodeClassname Node to compare.
	 * @param      boolean True if strict comparison should be used.
	 * @return     boolean
	 */
	public function equals(\$node, \$strict = false)
	{
		if (\$strict) {
			return (\$this->obj === \$node->obj);
		} else {
			return (\$this->obj == \$node->obj);
		}
	}
";
	}

	protected function addAttachParentNode(&$script)
	{
		$nodeClassname = $this->getStubNodeBuilder()->getClassname();
		$script .= "
	/**
	 * This method is used internally when constructing the tree structure
	 * from the database. To set the parent of a node, you should call
	 * addChildNode() on the parent.
	 *
	 * @param      $nodeClassname Parent node to attach.
	 * @return     void
	 * @throws     PropelException
	 */
	public function attachParentNode(\$node)
	{
		if (!\$node->hasChildNode(\$this, true))
			throw new PropelException('Failed to attach parent node for non-child.');

		\$this->parentNode = \$node;
	}
";
	}


	protected function addAttachChildNode(&$script)
	{
		$nodeClassname = $this->getStubNodeBuilder()->getClassname();
		$nodePeerClassname = $this->getStubNodePeerBuilder()->getClassname();
		$script .= "
	/**
	 * This method is used internally when constructing the tree structure
	 * from the database. To add a child to a node you should call the
	 * addChildNode() method instead.
	 *
	 * @param      $nodeClassname Child node to attach.
	 * @return     void
	 * @throws     PropelException
	 */
	public function attachChildNode(\$node)
	{
		if (\$this->hasChildNode(\$node))
			throw new PropelException('Failed to attach child node. Node already exists.');

		if (\$this->obj->isDeleted() || \$node->obj->isDeleted())
			throw new PropelException('Failed to attach node in deleted state.');

		if (\$this->obj->isNew() && !\$node->obj->isNew())
			throw new PropelException('Failed to attach non-new child to new node.');

		if (!\$this->obj->isNew() && \$node->obj->isNew())
			throw new PropelException('Failed to attach new child to non-new node.');

		if (\$this->getNodePath() . $nodePeerClassname::NPATH_SEP . \$node->getNodeIndex() != \$node->getNodePath())
			throw new PropelException('Failed to attach child node. Node path mismatch.');

		\$this->childNodes[\$node->getNodeIndex()] = \$node;
		ksort(\$this->childNodes);

		\$node->attachParentNode(\$this);
	}
";
	}

	protected function addDetachParentNode(&$script)
	{
		$nodeClassname = $this->getStubNodeBuilder()->getClassname();
		$script .= "
	/**
	 * This method is used internally when deleting nodes. It is used to break
	 * the link to this node's parent.
	 * @param      $nodeClassname Parent node to detach from.
	 * @return     void
	 * @throws     PropelException
	 */
	public function detachParentNode(\$node)
	{
		if (!\$node->hasChildNode(\$this, true))
			throw new PropelException('Failed to detach parent node from non-child.');

		unset(\$node->childNodes[\$this->getNodeIndex()]);
		\$this->parentNode = null;
	}
";
	}

	protected function addDetachChildNode(&$script)
	{
		$script .= "
	/**
	 * This method is used internally when deleting nodes. It is used to break
	 * the link to this between this node and the specified child.
	 * @param      ".$this->getStubNodeBuilder()->getClassname()." Child node to detach.
	 * @return     void
	 * @throws     PropelException
	 */
	public function detachChildNode(\$node)
	{
		if (!\$this->hasChildNode(\$node, true))
			throw new PropelException('Failed to detach non-existent child node.');

		unset(\$this->childNodes[\$node->getNodeIndex()]);
		\$node->parentNode = null;
	}
";
	}

	protected function addShiftChildNodes(&$script)
	{
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$nodePeerClassname = $this->getStubNodePeerBuilder()->getClassname();

		$script .= "
	/**
	 * Shifts child nodes in the specified direction and offset index. This
	 * method assumes that there is already space available in the
	 * direction/offset indicated.
	 *
	 * @param      int Direction/# spaces to shift. 1=leftshift, 1=rightshift
	 * @param      int Node index to start shift at.
	 * @param      Connection The connection to be used.
	 * @return     void
	 * @throws     PropelException
	 */
	protected function shiftChildNodes(\$direction, \$offsetIdx, \$con)
	{
		if (\$this->obj->isDeleted())
			throw new PropelException('Cannot shift nodes for deleted object');

		\$lastNode = \$this->getLastChildNode(true, \$con);
		\$lastIdx = (\$lastNode !== null ? \$lastNode->getNodeIndex() : 0);

		if (\$lastNode === null || \$offsetIdx > \$lastIdx)
			return;

		if (\$con === null)
			\$con = Propel::getConnection($peerClassname::DATABASE_NAME);

		if (!\$this->obj->isNew())
		{
			// Shift nodes in database.

			try {

				\$con->begin();

				\$n = \$lastIdx - \$offsetIdx + 1;
				\$i = \$direction < 1 ? \$offsetIdx : \$lastIdx;

				while (\$n--)
				{
					\$srcPath = \$this->getNodePath() . $nodePeerClassname::NPATH_SEP . \$i;			  // 1.2.2
					\$dstPath = \$this->getNodePath() . $nodePeerClassname::NPATH_SEP . (\$i+\$direction); // 1.2.3

					$nodePeerClassname::moveNodeSubTree(\$srcPath, \$dstPath, \$con);

					\$i -= \$direction;
				}

				\$con->commit();

			} catch (SQLException \$e) {
				\$con->rollback();
				throw new PropelException(\$e);
			}
		}

		// Shift the in-memory objects.

		\$n = \$lastIdx - \$offsetIdx + 1;
		\$i = \$direction < 1 ? \$offsetIdx : \$lastIdx;

		while (\$n--)
		{
			if (isset(\$this->childNodes[\$i]))
			{
				\$srcPath = \$this->getNodePath() . $nodePeerClassname::NPATH_SEP . \$i;			  // 1.2.2
				\$dstPath = \$this->getNodePath() . $nodePeerClassname::NPATH_SEP . (\$i+\$direction); // 1.2.3

				\$this->childNodes[\$i+\$direction] = \$this->childNodes[\$i];
				\$this->childNodes[\$i+\$direction]->adjustNodePath(\$srcPath, \$dstPath);

				unset(\$this->childNodes[\$i]);
			}

			\$i -= \$direction;
		}

		ksort(\$this->childNodes);
	}
";
	}

	protected function addInsertNewChildNode(&$script)
	{
		$peerClassname = $this->getStubPeerBuilder()->getClassname();
		$nodePeerClassname = $this->getStubNodePeerBuilder()->getClassname();
		$nodeClassname = $this->getStubNodePeerBuilder()->getClassname();

		$script .= "
	/**
	 * Inserts the node and its children at the specified childIdx.
	 *
	 * @param      $nodeClassname Node to insert.
	 * @param      int One-based child index to insert at.
	 * @param      Connection Connection to use.
	 * @param      void
	 */
	protected function insertNewChildNode(\$node, \$childIdx, \$con)
	{
		if (!\$node->obj->isNew())
			throw new PropelException('Failed to insert non-new node.');

		\$setNodePath = 'set' . $nodePeerClassname::NPATH_PHPNAME;

		\$node->obj->\$setNodePath(\$this->getNodePath() . $nodePeerClassname::NPATH_SEP . \$childIdx);
		\$node->obj->save(\$con);

		\$i = 1;
		foreach (\$node->childNodes as \$childNode)
			\$node->insertNewChildNode(\$childNode, \$i++, \$con);
	}
";
	}

	protected function addAdjustStatus(&$script)
	{
		$script .= "
	/**
	 * Adjust new/deleted status of node and all children.
	 *
	 * @param      string Status to change ('New' or 'Deleted')
	 * @param      boolean Value for status.
	 * @return     void
	 */
	protected function adjustStatus(\$status, \$b)
	{
		\$setStatus = 'set' . \$status;

		\$this->obj->\$setStatus(\$b);

		foreach (\$this->childNodes as \$childNode)
			\$childNode->obj->\$setStatus(\$b);
	}
";
	}

	protected function addAdjustNodePath(&$script)
	{
		$nodePeerClassname = $this->getStubNodePeerBuilder()->getClassname();
		$script .= "
	/**
	 * Adjust path of node and all children. This is used internally when
	 * inserting/moving nodes.
	 *
	 * @param      string Section of old path to change.
	 * @param      string New section to replace old path with.
	 * @return     void
	 */
	protected function adjustNodePath(\$oldBasePath, \$newBasePath)
	{
		\$setNodePath = 'set' . $nodePeerClassname::NPATH_PHPNAME;

		\$this->obj->\$setNodePath(\$newBasePath .  substr(\$this->getNodePath(), strlen(\$oldBasePath)));
		\$this->obj->resetModified($nodePeerClassname::NPATH_COLNAME);

		foreach (\$this->childNodes as \$childNode)
			\$childNode->adjustNodePath(\$oldBasePath, \$newBasePath);
	}
";
	}

} // PHP5NodeObjectBuilder
