<?php

// Template for creating base node Peer class on tree table.
//
// $Id: NodePeer.tpl,v 1.10 2005/02/13 12:23:52 micha Exp $

require_once 'propel/engine/builder/om/ClassTools.php';
require_once 'propel/engine/builder/om/PeerBuilder.php';

$npath_colname = '';
$npath_phpname = '';
$npath_len = 0;
$npath_sep = '';

foreach ($table->getColumns() as $col) {
    if ($col->isNodeKey()) {
        $npath_colname = $table->getName() . '.' . strtoupper($col->getName());
        $npath_phpname = $col->getPhpName();
        $npath_len = $col->getSize();
		$npath_sep = $col->getNodeKeySep();
        break;
    }
}

$db = $table->getDatabase();
if($table->getPackage()) {
    $package = $table->getPackage();
} else {
    $package = $targetPackage;
}

$CLASS = $table->getPhpName() . 'NodePeer';
echo '<' . '?' . 'php';

?>


require_once '<?php echo ClassTools::getFilePath($package, $table->getPhpName()) ?>';
require_once '<?php echo ClassTools::getFilePath($package, $table->getPhpName() . 'Node') ?>';


/**
 * Base static class for performing query operations on the tree contained by the 
 * '<?php echo $table->getPhpName() ?>' table.
 *
<?php if ($addTimeStamp) { ?>
 * This class was autogenerated by Propel on:
 *
 * [<?php echo $now ?>]
 *
<?php } ?>
 * @package <?php echo $package ?> 
 */
class <?php echo $basePrefix . $table->getPhpName() ?>NodePeer
{
    function NPATH_COLNAME() { return '<?php echo $npath_colname ?>'; }
    function NPATH_PHPNAME() { return '<?php echo $npath_phpname ?>'; }
	function NPATH_SEP() { return '<?php echo $npath_sep ?>'; }

    /** 
     * Temp function for CodeBase hacks that will go away.
     */
    function isCodeBase($con = null)
    {
        Propel::assertParam($con, '<?php echo $CLASS; ?>', 'isCodeBase', 1);
		$con =& Param::get($con);
		
		if ($con === null)
	        $con =& Propel::getConnection(<?php echo $table->getPhpName() ?>Peer::DATABASE_NAME());
            
        return (get_class($con) == 'ODBCConnection' && 
                get_class($con->getAdapter()) == 'CodeBaseAdapter');
    }
    
    /**
     * Create a new Node at the top of tree. This method will destroy any 
     * existing root node (along with its children). 
     *
     * Use at your own risk!
     *
     * @param <?php echo $table->getPhpName() ?> Object wrapped by new node.
     * @param Connection Connection to use.
     * @return <?php echo $table->getPhpName() ?>Node
     * @throws PropelException
     */
    function & createNewRootNode(&$obj, $con = null)
    {
        Propel::assertParam($con, '<?php echo $CLASS; ?>', 'createNewRootNode', 2);
	    $con =& Param::get($con);
	    
        if ($con === null)
            $con =& Propel::getConnection(<?php echo $table->getPhpName() ?>Peer::DATABASE_NAME());
  
        if (Propel::isError($con)) {
            return $con;
        }

        $e = $con->begin();
        if (Creole::isError($e)) { $con->rollback(); return new PropelException(PROPEL_ERROR_DB, $e); }

        $e = <?php echo $table->getPhpName() ?>NodePeer::deleteNodeSubTree('1', Param::set($con));
        if (Propel::isError($e)) { $con->rollback(); return $e; }

        $setNodePath = 'set' . <?php echo $table->getPhpName() ?>NodePeer::NPATH_PHPNAME();
        $obj->$setNodePath('1');

        $e = $obj->save(Param::set($con));
        if (Propel::isError($e)) { $con->rollback(); return $e; }

        $e = $con->commit();
        if (Creole::isError($e)) { $con->rollback(); return new PropelException(PROPEL_ERROR_DB, $e); }

        return new <?php echo $table->getPhpName() ?>Node(Param::set($obj));
    }
    
    /**
     * Inserts a new Node at the top of tree. Any existing root node (along with
     * its children) will be made a child of the new root node. This is a 
     * safer alternative to createNewRootNode().
     *
     * @param <?php echo $table->getPhpName() ?> Object wrapped by new node.
     * @param Connection Connection to use.
     * @return <?php echo $table->getPhpName() ?>Node
     * @throws PropelException
     */
    function & insertNewRootNode(&$obj, $con = null)
    {
        Propel::assertParam($con, '<?php echo $CLASS; ?>', 'insertNewRootNode', 2);
	    $con =& Param::get($con);
	    
		if ($con === null)
            $con =& Propel::getConnection(<?php echo $table->getPhpName() ?>Peer::DATABASE_NAME());
        
        $e = $con->begin();
        if (Creole::isError($e)) { $con->rollback(); return new PropelException(PROPEL_ERROR_DB, $e); }

        // Move root tree to an invalid node path.
        $e = <?php echo $table->getPhpName() ?>NodePeer::moveNodeSubTree('1', '0', Param::set($con));
        if (Propel::isError($e)) { $con->rollback(); return $e; }

        $setNodePath = 'set' . <?php echo $table->getPhpName() ?>NodePeer::NPATH_PHPNAME();
        
        // Insert the new root node.
        $obj->$setNodePath('1');

        $e = $obj->save(Param::set($con));        
        if (Propel::isError($e)) { $con->rollback(); return $e; }

        // Move the old root tree as a child of the new root.
        $e = <?php echo $table->getPhpName() ?>NodePeer::moveNodeSubTree('0', '1' . <?php echo $table->getPhpName() ?>NodePeer::NPATH_SEP() . '1', Param::set($con));
        if (Propel::isError($e)) { $con->rollback(); return $e; }

        $e = $con->commit();
        if (Creole::isError($e)) { $con->rollback(); return new PropelException(PROPEL_ERROR_DB, $e); }
        
        return new <?php echo $table->getPhpName() ?>Node(Param::set($obj));
    }

    /**
     * Retrieves an array of tree nodes based on specified criteria. Optionally 
     * includes all parent and/or child nodes of the matching nodes.
     *
     * @param Criteria Criteria to use.
     * @param boolean True if ancestors should also be retrieved.
     * @param boolean True if descendants should also be retrieved.
     * @param Connection Connection to use.
     * @return array Array of root nodes.
     */
    function & retrieveNodes(&$criteria, $ancestors = false, $descendants = false, $con = null)
    {
        Propel::assertParam($con, '<?php echo $CLASS; ?>', 'retrieveNodes', 4);
	    $con =& Param::get($con);
        $criteria =& <?php echo $table->getPhpName() ?>NodePeer::buildFamilyCriteria($criteria, $ancestors, $descendants);
        $rs =& <?php echo $table->getPhpName() ?>Peer::doSelectRS($criteria, Param::set($con));
        return <?php echo $table->getPhpName() ?>NodePeer::populateNodes($rs, $criteria);
    }

    /**
     * Retrieves a tree node based on a primary key. Optionally includes all 
     * parent and/or child nodes of the matching node.
     *
     * @param mixed <?php echo $table->getPhpName() ?> primary key (array for composite keys)
     * @param boolean True if ancestors should also be retrieved.
     * @param boolean True if descendants should also be retrieved.
     * @param Connection Connection to use.
     * @return <?php echo $table->getPhpName() ?>Node
     */
    function & retrieveNodeByPK($pk, $ancestors = false, $descendants = false, $con = null)
    {
        Propel::assertParam($con, '<?php echo $CLASS; ?>', 'retrieveNodeByPK', 4);
	    $con =& Param::get($con);
        return new PropelException(PROPEL_ERROR, 'retrieveNodeByPK() not implemented yet.');
    }

    /**
     * Retrieves a tree node based on a node path. Optionally includes all 
     * parent and/or child nodes of the matching node.
     *
     * @param string Node path to retrieve.
     * @param boolean True if ancestors should also be retrieved.
     * @param boolean True if descendants should also be retrieved.
     * @param Connection Connection to use.
     * @return <?php echo $table->getPhpName() ?>Node
     */
    function & retrieveNodeByNP($np, $ancestors = false, $descendants = false, $con = null)
    {
        Propel::assertParam($con, '<?php echo $CLASS; ?>', 'retrieveNodeByNP', 4);
	    $con =& Param::get($con);
        $criteria =& new Criteria(<?php echo $table->getPhpName() ?>Peer::DATABASE_NAME());
        $criteria->add(<?php echo $table->getPhpName() ?>NodePeer::NPATH_COLNAME(), $np, Criteria::EQUAL());
        $criteria =& <?php echo $table->getPhpName() ?>NodePeer::buildFamilyCriteria($criteria, $ancestors, $descendants);
        $rs =& <?php echo $table->getPhpName() ?>Peer::doSelectRS($criteria, Param::set($con));
        $nodes =& <?php echo $table->getPhpName() ?>NodePeer::populateNodes($rs, $criteria);
        return (count($nodes) == 1 ? $nodes[0] : null);
    }

    /**
     * Retrieves the root node.
     *
     * @param string Node path to retrieve.
     * @param boolean True if descendants should also be retrieved.
     * @param Connection Connection to use.
     * @return <?php echo $table->getPhpName() ?>Node
     */ 
    function & retrieveRootNode($descendants = false, $con = null)
    {
        Propel::assertParam($con, '<?php echo $CLASS; ?>', 'retrieveRootNode', 2);
	    $con =& Param::get($con);
        return <?php echo $table->getPhpName() ?>NodePeer::retrieveNodeByNP('1', false, $descendants, Param::set($con));
    }

    /**
     * Moves the node subtree at srcpath to the dstpath. This method is intended
     * for internal use by the BaseNode object. Note that it does not check for 
     * preexisting nodes at the dstpath. It also does not update the  node path 
     * of any Node objects that might currently be in memory. 
     *
     * Use at your own risk!
     *
     * @param string Source node path to move (root of the src subtree).
     * @param string Destination node path to move to (root of the dst subtree).
     * @param Connection Connection to use.
     * @return void
     * @throws PropelException
     * @todo This is currently broken for simulated "onCascadeDelete"s. 
     * @todo Need to abstract the SQL better. The CONCAT sql function doesn't 
     *       seem to be standardized (i.e. mssql), so maybe it needs to be moved
     *       to DBAdapter.
     */    
    function moveNodeSubTree($srcPath, $dstPath, $con = null)
    {
        Propel::assertParam($con, '<?php echo $CLASS; ?>', 'moveNodeSubTree', 3);
	    $con =& Param::get($con);

        if (substr($dstPath, 0, strlen($srcPath)) == $srcPath)
            return new PropelException(PROPEL_ERROR, 'Cannot move a node subtree within itself.');
        
		if ($con === null)
            $con =& Propel::getConnection(<?php echo $table->getPhpName() ?>Peer::DATABASE_NAME());

        /**
         * Example:
         * UPDATE table 
         * SET npath = CONCAT('1.3', SUBSTRING(npath, 6, 74)) 
         * WHERE npath = '1.2.2' OR npath LIKE '1.2.2.%'
         */
         
        $npath = <?php echo $table->getPhpName() ?>NodePeer::NPATH_COLNAME();
		//the following dot isn`t mean`t a nodeKeySeperator
        $setcol = substr($npath, strpos($npath, '.')+1);
        $setcollen = <?php echo $npath_len ?>;
        $db = Propel::getDb(<?php echo $table->getPhpName() ?>Peer::DATABASE_NAME());
        
        // <hack> 
        if (<?php echo $table->getPhpName() ?>NodePeer::isCodeBase(Param::set($con)))
        {
            // This is a hack to get CodeBase working. It will eventually be removed.
            // It is a workaround for the following CodeBase bug:
            //   -Prepared statement parameters cannot be embedded in SQL functions (i.e. CONCAT)
            $sql = "UPDATE " . <?php echo $table->getPhpName() ?>Peer::TABLE_NAME() . " " .
                   "SET $setcol=" . $db->concatString("'$dstPath'", $db->subString($npath, strlen($srcPath)+1, $setcollen)) . " " . 
                   "WHERE $npath = '$srcPath' OR $npath LIKE '$srcPath" . <?php echo $table->getPhpName() ?>NodePeer::NPATH_SEP() . "%'";

            $con->executeUpdate($sql);
        }
        else
        {
        // </hack>
            $sql = "UPDATE " . <?php echo $table->getPhpName() ?>Peer::TABLE_NAME() . " " .
                   "SET $setcol=" . $db->concatString('?', $db->subString($npath, '?', '?')) . " " . 
                   "WHERE $npath = ? OR $npath LIKE ?";

            $stmt =& $con->prepareStatement($sql);
            $stmt->setString(1, $dstPath);
            $stmt->setInt(2, strlen($srcPath)+1);
            $stmt->setInt(3, $setcollen);
            $stmt->setString(4, $srcPath);
            $stmt->setString(5, $srcPath . <?php echo $table->getPhpName() ?>NodePeer::NPATH_SEP() . '%');
            $stmt->executeUpdate();
        // <hack>
        }
        // </hack>
    }

    /**
     * Deletes the node subtree at the specified node path from the database.
     *
     * @param string Node path to delete
     * @param Connection Connection to use.
     * @return void
     * @throws PropelException
     * @todo This is currently broken for simulated "onCascadeDelete"s. 
     */
    function deleteNodeSubTree($nodePath, $con = null)
    {
        Propel::assertParam($con, '<?php echo $CLASS; ?>', 'deleteNodeSubTree', 2);
	    $con =& Param::get($con);

		if ($con === null)
            $con =& Propel::getConnection(<?php echo $table->getPhpName() ?>Peer::DATABASE_NAME());

        /**
         * DELETE FROM table
         * WHERE npath = '1.2.2' OR npath LIKE '1.2.2.%'
         */
        
        $criteria =& new Criteria(<?php echo $table->getPhpName() ?>Peer::DATABASE_NAME());
        $criteria->add(<?php echo $table->getPhpName() ?>NodePeer::NPATH_COLNAME(), $nodePath, Criteria::EQUAL());
        $criteria->addOr(<?php echo $table->getPhpName() ?>NodePeer::NPATH_COLNAME(), $nodePath . <?php echo $table->getPhpName() ?>NodePeer::NPATH_SEP() . '%', Criteria::LIKE());
        // For now, we call BasePeer directly since <?php echo $table->getPhpName() ?>Peer tries to 
        // do a cascade delete.
        //          <?php echo $table->getPhpName() ?>Peer::doDelete($criteria, Param::set($con));
        BasePeer::doDelete($criteria, $con);
    }
      
    /** 
     * Builds the criteria needed to retrieve node ancestors and/or descendants.
     *
     * @param Criteria Criteria to start with
     * @param boolean True if ancestors should be retrieved.
     * @param boolean True if descendants should be retrieved.
     * @return Criteria
     */
    function & buildFamilyCriteria(&$criteria, $ancestors = false, $descendants = false)
    {
        /*
            Example SQL to retrieve nodepath '1.2.3' with both ancestors and descendants:
            
            SELECT L.NPATH, L.LABEL, test.NPATH, UCASE(L.NPATH) 
            FROM test L, test 
            WHERE test.NPATH='1.2.3' AND 
                 (L.NPATH=SUBSTRING(test.NPATH, 1, LENGTH(L.NPATH)) OR 
                  test.NPATH=SUBSTRING(L.NPATH, 1, LENGTH(test.NPATH))) 
            ORDER BY UCASE(L.NPATH) ASC
        */

        if ($criteria === null)
            $criteria =& new Criteria(<?php echo $table->getPhpName() ?>::DATABASE_NAME());

        if (!$criteria->getSelectColumns())
            <?php echo $table->getPhpName() ?>Peer::addSelectColumns($criteria);

        $db =& Propel::getDb($criteria->getDbName());

        if (($ancestors || $descendants) && $criteria->size())
        {
            // If we are retrieving ancestors/descendants, we need to do a 
            // self-join to locate them. The exception to this is if no search
            // criteria is specified. In this case we're retrieving all nodes 
            // anyway, so there is no need to do a self-join. 
            
            // The left-side of the self-join will contain the columns we'll
            // use to build node objects (target node records along with their
            // ancestors and/or descendants). The right-side of the join will
            // contain the target node records specified by the initial criteria.
            // These are used to match the appropriate ancestor/descendant on
            // the left. 

            // Specify an alias for the left-side table to use.
            $criteria->addAlias('L', <?php echo $table->getPhpName() ?>Peer::TABLE_NAME());
            
            // Make sure we have select columns to begin with.
            if (!$criteria->getSelectColumns())
                <?php echo $table->getPhpName() ?>Peer::addSelectColumns($criteria);
            
            // Replace any existing columns for the right-side table with the 
            // left-side alias.
            $selectColumns = $criteria->getSelectColumns();
            $criteria->clearSelectColumns();
            foreach ($selectColumns as $colName)
                $criteria->addSelectColumn(str_replace(<?php echo $table->getPhpName() ?>Peer::TABLE_NAME(), 'L', $colName));

            $a = null;
            $d = null;
            
            $npathL = <?php echo $table->getPhpName() ?>Peer::alias('L', <?php echo $table->getPhpName() ?>NodePeer::NPATH_COLNAME());
            $npathR = <?php echo $table->getPhpName() ?>NodePeer::NPATH_COLNAME();
            $npath_len = <?php echo $npath_len ?>;
            
            if ($ancestors)
            {
                // For ancestors, match left-side node paths which are contained 
                // by right-side node paths.
                $a =& $criteria->getNewCriterion($npathL, 
                                                "$npathL=" . $db->subString($npathR, 1, $db->strLength($npathL), $npath_len),
                                                Criteria::CUSTOM());                                                        
            }
            
            if ($descendants)
            {
                // For descendants, match left-side node paths which contain 
                // right-side node paths.
                $d =& $criteria->getNewCriterion($npathR,
                                                "$npathR=" . $db->subString($npathL, 1, $db->strLength($npathR), $npath_len),
                                                Criteria::CUSTOM());
            }

            if ($a)
            {
                if ($d) $a->addOr($d);
                $criteria->addAnd($a);
            }
            else if ($d)
            {
                $criteria->addAnd($d);
            }
            
            // Add the target node path column. This is used by populateNodes().
            $criteria->addSelectColumn($npathR);
            
            // Sort by node path to speed up tree construction in populateNodes()
            $criteria->addAsColumn('npathlen', $db->strLength($npathL));
            $criteria->addAscendingOrderByColumn('npathlen');
            $criteria->addAscendingOrderByColumn($npathL);
        }
        else
        {
            // Add the target node path column. This is used by populateNodes().
            $criteria->addSelectColumn(<?php echo $table->getPhpName() ?>NodePeer::NPATH_COLNAME());

            // Sort by node path to speed up tree construction in populateNodes()
            $criteria->addAsColumn('npathlen', $db->strLength(<?php echo $table->getPhpName() ?>NodePeer::NPATH_COLNAME()));
            $criteria->addAscendingOrderByColumn('npathlen');
            $criteria->addAscendingOrderByColumn(<?php echo $table->getPhpName() ?>NodePeer::NPATH_COLNAME());
        }
        
        return $criteria;
    }    
    
    /**
     * This method reconstructs as much of the tree structure as possible from
     * the given array of objects. Depending on how you execute your query, it 
     * is possible for the ResultSet to contain multiple tree fragments (i.e.
     * subtrees). The array returned by this method will contain one entry
     * for each subtree root node it finds. The remaining subtree nodes are
     * accessible from the <?php echo $table->getPhpName() ?>Node methods of the 
     * subtree root nodes.
     *
     * @param array Array of <?php echo $table->getPhpName() ?>Node objects
     * @return array Array of <?php echo $table->getPhpName() ?>Node objects
     */
    function & buildTree(&$nodes)
    {
        // Subtree root nodes to return
        $rootNodes = array();
        $copy = $nodes;

        // Build the tree relations
        foreach ($nodes as $key1 => $node)
        {
			//strrpos fix
			$sep = 0;
			while(false !== ($last = strpos($node->getNodePath(), <?php echo $table->getPhpName() ?>NodePeer::NPATH_SEP(), $sep))) {
				$sep = $last + strlen(<?php echo $table->getPhpName() ?>NodePeer::NPATH_SEP());
			}
            $parentPath = ($sep != 0 ? substr($node->getNodePath(), 0, $sep - strlen(<?php echo $table->getPhpName() ?>NodePeer::NPATH_SEP())) : '');
            $parentNode =& Propel::null();

            // Scan other nodes for parent.
            foreach ($copy as $key2 => $pnode)
            {
                if ($pnode->getNodePath() == $parentPath)
                {
                    $parentNode =& $nodes[$key2];
                    break;
                }
            }

            // If parent was found, attach as child, otherwise its a subtree root
            if ($parentNode)
                $parentNode->attachChildNode($nodes[$key1]);
            else
                $rootNodes[] =& $nodes[$key1];
        }

        return $rootNodes;
    }

    /**
     * Populates the <?php echo $table->getPhpName() ?> objects from the 
     * specified ResultSet, wraps them in <?php echo $table->getPhpName() ?>Node 
     * objects and build the appropriate node relationships.
     * The array returned by this method will only include the initial targets 
     * of the query, even if ancestors/descendants were also requested. 
     * The ancestors/descendants will be cached in memory and are accessible via
     * the getNode() methods. 
     *
     * @param ResultSet 
     * @param Criteria
     * @return array Array of <?php $table->getPhpName() ?>Node objects.
     */
    function & populateNodes(&$rs, &$criteria)
    {
        $nodes = array();
        $targets = array();
        $values = array();
		$targetfld = count($criteria->getSelectColumns());
        
<?php if (!$table->getChildrenColumn()) { ?>
		// set the class once to avoid overhead in the loop
		$cls = <?php echo $table->getPhpName() ?>Peer::getOMClass();
		if (Propel::isError($cls =& Propel::import($cls))) {
		  return $cls;
		}
<?php } ?>
        
        // populate the object(s)
        while($rs->next()) 
        {
            if (!isset($nodes[$rs->getString(1)]))
            {
<?php if ($table->getChildrenColumn()) { ?>
				// class must be set each time from the record row
				$cls =& Propel::import(<?php echo $table->getPhpName() ?>Peer::getOMClass($rs, 1));
				if (Propel::isError($cls)) {
					return $cls;
				}
<?php } ?>
				$obj =& new $cls();
				
				if (Propel::isError($e =& $obj->hydrate($rs))) {
					return $e;
				}

                $nodes[$rs->getString(1)] =& new <?php echo $table->getPhpName() ?>Node(Param::set($obj));
			}
            $node =& $nodes[$rs->getString(1)];
            
            if ($node->getNodePath() == $rs->getString($targetfld))
                $targets[$node->getNodePath()] =& $node;
        }

        <?php echo $table->getPhpName() ?>NodePeer::buildTree($nodes);
        
        foreach($targets as $key => $value)
                $values[] =& $targets[$key];
                
        return $values;
    }

}

?>