Author: dr
Date: Fri Oct  5 17:16:45 2007
New Revision: 6378

Log:
- Renamed the ezcTreeInvalidIdException to ezcTreeUnknownIdException and
  added the ezcTreeInvalidIdException that tells you when the ID itself
  is broken.
- Added an option that allows you to change the separation character for
  the materialized path backend.

Modified:
    trunk/Tree/design/class_diagram.png
    trunk/Tree/review.txt
    trunk/Tree/src/backends/memory.php
    trunk/Tree/src/backends/xml.php
    trunk/Tree/src/exceptions/invalid_id.php
    trunk/Tree/src/tree.php
    trunk/Tree/src/tree_autoload.php
    trunk/Tree/tests/tree.php
    trunk/TreeDatabaseTiein/src/backends/db_materialized_path.php
    trunk/TreeDatabaseTiein/src/backends/db_nested_set.php
    trunk/TreeDatabaseTiein/tests/db_materialized_path_tree.php
    trunk/TreeDatabaseTiein/tests/suite.php

Modified: trunk/Tree/design/class_diagram.png
==============================================================================
Binary files - no diff available.

Modified: trunk/Tree/review.txt
==============================================================================
--- trunk/Tree/review.txt [iso-8859-1] (original)
+++ trunk/Tree/review.txt [iso-8859-1] Fri Oct  5 17:16:45 2007
@@ -13,7 +13,7 @@
        It was, but I added an option to make both subtrees and just nodes
        highlighted now.
 
-[ ] Files missing __get() and __set() ($this->properties is accessed directly):
+[X] Files missing __get() and __set() ($this->properties is accessed directly):
      - Tree/src/backends/memory.php
      - Tree/src/backends/xml.php (__get() is present, but not fully 
implemented)
      - Tree/src/tree_node.php (__get() and set() are implemented but not used)
@@ -48,7 +48,7 @@
 Other points
 ------------
 
-[ ] Issue #11444: Allow the / char to be changed in the nested set
+[X] Issue #11444: Allow the / char to be changed in the materialized path
        implementation, and add a check for this char in used IDs when creating 
a
        new node.
 
@@ -57,7 +57,7 @@
 [ ] Check XHTML visitor's generation of paths - they are sort of useles as 
only the
     last item is used. Perhaps make this an option.
 
-[ ] Check if the highlighting can be done on the data, and not the <li> nodes -
+[X] Check if the highlighting can be done on the data, and not the <li> nodes -
     make that an option as well perhaps.
 
 [ ] Check example from Alex, and add them to docs if useful.

Modified: trunk/Tree/src/backends/memory.php
==============================================================================
--- trunk/Tree/src/backends/memory.php [iso-8859-1] (original)
+++ trunk/Tree/src/backends/memory.php [iso-8859-1] Fri Oct  5 17:16:45 2007
@@ -113,7 +113,7 @@
     {
         if ( !$this->nodeExists( $nodeId ) )
         {
-            throw new ezcTreeInvalidIdException( $nodeId );
+            throw new ezcTreeUnknownIdException( $nodeId );
         }
         return $this->nodeList[$nodeId];
     }

Modified: trunk/Tree/src/backends/xml.php
==============================================================================
--- trunk/Tree/src/backends/xml.php [iso-8859-1] (original)
+++ trunk/Tree/src/backends/xml.php [iso-8859-1] Fri Oct  5 17:16:45 2007
@@ -269,7 +269,7 @@
         $node = $this->dom->getElementById( 
"{$this->properties['prefix']}$nodeId" );
         if ( !$node )
         {
-            throw new ezcTreeInvalidIdException( $nodeId );
+            throw new ezcTreeUnknownIdException( $nodeId );
         }
         return $node;
     }

Modified: trunk/Tree/src/exceptions/invalid_id.php
==============================================================================
--- trunk/Tree/src/exceptions/invalid_id.php [iso-8859-1] (original)
+++ trunk/Tree/src/exceptions/invalid_id.php [iso-8859-1] Fri Oct  5 17:16:45 
2007
@@ -10,8 +10,7 @@
  */
 
 /**
- * Exception that is thrown when a node is request through an invalid
- * (non-existing) ID.
+ * Exception that is thrown when a node is created with an invalid ID.
  *
  * @package Tree
  * @version //autogentag//
@@ -23,9 +22,9 @@
      *
      * @param string $nodeId
      */
-    public function __construct( $nodeId )
+    public function __construct( $nodeId, $invalidChar )
     {
-        parent::__construct( "The node with ID '{$nodeId}' could not be 
found." );
+        parent::__construct( "The node ID '{$nodeId}' contains the invalid 
character '{$invalidChar}'." );
     }
 }
 ?>

Modified: trunk/Tree/src/tree.php
==============================================================================
--- trunk/Tree/src/tree.php [iso-8859-1] (original)
+++ trunk/Tree/src/tree.php [iso-8859-1] Fri Oct  5 17:16:45 2007
@@ -166,6 +166,18 @@
     }
 
     /**
+     * This method checks whether a node ID is valid to be used in a backend.
+     *
+     * @throws ezcTreeInvalidNodeIDException if the node is not valid.
+     *
+     * @param string $nodeId
+     */
+    protected function checkNodeId( $nodeId )
+    {
+        /* The default implementation does not check anything */
+    }
+
+    /**
      * Creates a new tree node with node ID $nodeId and $data.
      *
      * This method returns by default an object of the ezcTreeNode class, 
@@ -179,6 +191,7 @@
      */
     public function createNode( $nodeId, $data )
     {
+        $this->checkNodeID( $nodeId );
         $className = $this->properties['nodeClassName'];
         return new $className( $this, $nodeId, $data );
     }
@@ -213,7 +226,7 @@
     {
         if ( !$this->nodeExists( $nodeId ) )
         {
-            throw new ezcTreeInvalidIdException( $nodeId );
+            throw new ezcTreeUnknownIdException( $nodeId );
         }
         $className = $this->properties['nodeClassName'];
         $node = new $className( $this, $nodeId );

Modified: trunk/Tree/src/tree_autoload.php
==============================================================================
--- trunk/Tree/src/tree_autoload.php [iso-8859-1] (original)
+++ trunk/Tree/src/tree_autoload.php [iso-8859-1] Fri Oct  5 17:16:45 2007
@@ -19,6 +19,7 @@
     'ezcTreeInvalidXmlFormatException'          => 
'Tree/exceptions/invalid_xml_format.php',
     'ezcTreeTransactionAlreadyStartedException' => 
'Tree/exceptions/transaction_already_started.php',
     'ezcTreeTransactionNotStartedException'     => 
'Tree/exceptions/transaction_not_started.php',
+    'ezcTreeUnknownIdException'                 => 
'Tree/exceptions/unknown_id.php',
     'ezcTreeDataStore'                          => 
'Tree/interfaces/data_store.php',
     'ezcTreeVisitable'                          => 
'Tree/interfaces/visitable.php',
     'ezcTree'                                   => 'Tree/tree.php',

Modified: trunk/Tree/tests/tree.php
==============================================================================
--- trunk/Tree/tests/tree.php [iso-8859-1] (original)
+++ trunk/Tree/tests/tree.php [iso-8859-1] Fri Oct  5 17:16:45 2007
@@ -164,7 +164,7 @@
             $node = $tree->fetchNodeById( 42 );
             self::fail( "Expected exception was not thrown." );
         }
-        catch ( ezcTreeInvalidIdException $e )
+        catch ( ezcTreeUnknownIdException $e )
         {
             self::assertSame( "The node with ID '42' could not be found.", 
$e->getMessage() );
         }
@@ -192,7 +192,7 @@
         {
             self::assertSame( true, $tree->fetchNodeById( 98 )->isChildOf( 
$tree->fetchNodeById( 99 ) ) );
         }
-        catch ( ezcTreeInvalidIdException $e )
+        catch ( ezcTreeUnknownIdException $e )
         {
             self::assertSame( "The node with ID '98' could not be found.", 
$e->getMessage() );
         }
@@ -220,7 +220,7 @@
         {
             self::assertSame( false, $tree->isChildOf( 98, 99 ) );
         }
-        catch ( ezcTreeInvalidIdException $e )
+        catch ( ezcTreeUnknownIdException $e )
         {
             self::assertSame( "The node with ID '98' could not be found.", 
$e->getMessage() );
         }

Modified: trunk/TreeDatabaseTiein/src/backends/db_materialized_path.php
==============================================================================
--- trunk/TreeDatabaseTiein/src/backends/db_materialized_path.php [iso-8859-1] 
(original)
+++ trunk/TreeDatabaseTiein/src/backends/db_materialized_path.php [iso-8859-1] 
Fri Oct  5 17:16:45 2007
@@ -23,6 +23,9 @@
  *
  * @property-read ezcTreeDbDataStore $store
  *                The data store that is used for retrieving/storing data.
+ * @property-read string             $separationChar
+ *                The character that is used to separate node IDs internally.
+ *                This character can then *not* be part of a node ID.
  * @property      string $nodeClassName
  *                Which class is used as tree node - this class *must* inherit
  *                the ezcTreeNode class.
@@ -34,12 +37,116 @@
 class ezcTreeDbMaterializedPath extends ezcTreeDb
 {
     /**
+     * Constructs a new ezcTreeDbMaterializedPath object.
+     *
+     * The different arguments to the constructor configure which database
+     * connection ($dbh) is used to access the database and the $indexTableName
+     * argument which table is used to retrieve the relation data from. The
+     * $store argument configure which data store is used with this tree.
+     *
+     * The $separationChar argument defaults to / and is used to separate node
+     * IDs internally. This character can *not* be part of a node ID, and 
should be
+     * the same character that was used when creating the tree.
+     *
+     * Just like the others, this database backend requires the index table to
+     * at least define the field 'id', which can either be a string or an
+     * integer field.
+     * 
+     * @param ezcDbHandler       $dbh
+     * @param string             $indexTableName
+     * @param ezcTreeDbDataStore $store
+     * @param string             $separationChar
+     */
+    public function __construct( ezcDbHandler $dbh, $indexTableName, 
ezcTreeDbDataStore $store, $separationChar = '/' )
+    {
+        parent::__construct( $dbh, $indexTableName, $store );
+        $this->properties['separationChar'] = $separationChar;
+    }
+
+    /**
+     * Returns the value of the property $name.
+     *
+     * @throws ezcBasePropertyNotFoundException if the property does not exist.
+     * @param string $name
+     * @ignore
+     */
+    public function __get( $name )
+    {
+        switch ( $name )
+        {
+            case 'separationChar':
+                return $this->properties[$name];
+        }
+        return parent::__get( $name );
+    }
+
+    /**
+     * Sets the property $name to $value.
+     *
+     * @throws ezcBasePropertyNotFoundException if the property does not exist.
+     * @throws ezcBasePropertyPermissionException if a read-only property is
+     *         tried to be modified.
+     * @param string $name
+     * @param mixed $value
+     * @ignore
+     */
+    public function __set( $name, $value )
+    {
+        switch ( $name )
+        {
+            case 'separationChar':
+                throw new ezcBasePropertyPermissionException( $name, 
ezcBasePropertyPermissionException::READ );
+
+            default:
+                return parent::__set( $name, $value );
+        }
+    }
+
+    /**
+     * Returns true if the property $name is set, otherwise false.
+     *
+     * @param string $name     
+     * @return bool
+     * @ignore
+     */
+    public function __isset( $name )
+    {
+        switch ( $name )
+        {
+            case 'separationChar':
+                return isset( $this->properties[$name] );
+
+            default:
+                return parent::__isset( $name );
+        }
+    }
+
+    /**
+     * This method checks whether a node ID is valid to be used in a backend.
+     *
+     * @throws ezcTreeInvalidNodeIDException if the node is not valid.
+     *
+     * @param string $nodeId
+     */
+    protected function checkNodeId( $nodeId )
+    {
+        if ( strchr( $nodeId, $this->properties['separationChar'] ) != false )
+        {
+            throw new ezcTreeInvalidIdException( $nodeId, 
$this->properties['separationChar'] );
+        }
+    }
+
+    /**
      * Creates a new ezcTreeDbMaterializedPath object.
      *
      * The different arguments to the method configure which database
      * connection ($dbh) is used to access the database and the $indexTableName
      * argument which table is used to retrieve the relation data from. The
      * $store argument configure which data store is used with this tree.
+     *
+     * The $separationChar argument defaults to / and is used to separate node
+     * IDs internally. This character can *not* be part of a node ID, and the 
same
+     * character should be used when re-opening the tree upon instantiation.
      *
      * It is up to the user to create the database table and make sure it is
      * empty.
@@ -47,10 +154,11 @@
      * @param ezcDbHandler       $dbh
      * @param string             $indexTableName
      * @param ezcTreeDbDataStore $store
-     */
-    public static function create( ezcDbHandler $dbh, $indexTableName, 
ezcTreeDbDataStore $store )
-    {
-        return new ezcTreeDbMaterializedPath( $dbh, $indexTableName, $store );
+     * @param string             $separationChar
+     */
+    public static function create( ezcDbHandler $dbh, $indexTableName, 
ezcTreeDbDataStore $store, $separationChar = '/' )
+    {
+        return new ezcTreeDbMaterializedPath( $dbh, $indexTableName, $store, 
$separationChar );
     }
 
     /**
@@ -136,7 +244,7 @@
         // Fetch node information
         list( $parentId, $path ) = $this->fetchNodeInformation( $nodeId );
 
-        $parts = split( '/', $path );
+        $parts = split( $this->properties['separationChar'], $path );
         array_shift( $parts );
 
         foreach ( $parts as $pathNodeId )
@@ -174,7 +282,7 @@
         // WHERE path LIKE '$path/%'
         $q->select( 'id' )
           ->from( $db->quoteIdentifier( $this->indexTableName ) )
-          ->where( $q->expr->like( 'path', $q->bindValue( "$path/%" ) ) );
+          ->where( $q->expr->like( 'path', $q->bindValue( 
"$path{$this->properties['separationChar']}%" ) ) );
         $s = $q->prepare();
         $s->execute();
 
@@ -277,7 +385,7 @@
         // WHERE path LIKE '$path/%'
         $q->select( 'count(id)' )
           ->from( $db->quoteIdentifier( $this->indexTableName ) )
-          ->where( $q->expr->like( 'path', $q->bindValue( "$path/%" ) ) );
+          ->where( $q->expr->like( 'path', $q->bindValue( 
"$path{$this->properties['separationChar']}%" ) ) );
         $s = $q->prepare();
         $s->execute();
         $r = $s->fetch( PDO::FETCH_NUM );
@@ -296,7 +404,7 @@
         // Fetch information for node
         list( $parentId, $path ) = $this->fetchNodeInformation( $nodeId );
 
-        return substr_count( $path, '/' ) - 1;
+        return substr_count( $path, $this->properties['separationChar'] ) - 1;
     }
 
     /**
@@ -338,7 +446,7 @@
         // Fetch node information
         list( $dummyParentId, $path ) = $this->fetchNodeInformation( $childId 
);
 
-        $parts = split( '/', $path );
+        $parts = split( $this->properties['separationChar'], $path );
         array_shift( $parts );
 
         return in_array( $parentId, $parts ) && ( $childId !== $parentId );
@@ -378,7 +486,7 @@
         $q->insertInto( $db->quoteIdentifier( $this->indexTableName ) )
           ->set( 'parent_id', "null" )
           ->set( 'id', $q->bindValue( $node->id ) )
-          ->set( 'path', $q->bindValue( '/' . $node->id ) );
+          ->set( 'path', $q->bindValue( $this->properties['separationChar'] . 
$node->id ) );
         $s = $q->prepare();
         $s->execute();
 
@@ -408,7 +516,7 @@
         $q->insertInto( $db->quoteIdentifier( $this->indexTableName ) )
           ->set( 'parent_id', $q->bindValue( $parentId ) )
           ->set( 'id', $q->bindValue( $childNode->id ) )
-          ->set( 'path', $q->bindValue( $path . '/' . $childNode->id ) );
+          ->set( 'path', $q->bindValue( $path . 
$this->properties['separationChar'] . $childNode->id ) );
         $s = $q->prepare();
         $s->execute();
 
@@ -476,7 +584,7 @@
 
         // Get path to parent of $nodeId
         // - position of last /
-        $pos = strrpos( $origPath, '/' );
+        $pos = strrpos( $origPath, $this->properties['separationChar'] );
         // - parent path and its length
         $parentPath = substr( $origPath, 0, $pos );
         $parentPathLength = strlen( $parentPath ) + 1;
@@ -504,7 +612,7 @@
             ) )
           ->where( $q->expr->lOr(
                 $q->expr->eq( 'id', $q->bindValue( $nodeId ) ),
-                $q->expr->like( 'path', $q->bindValue( "$origPath/%" ) )
+                $q->expr->like( 'path', $q->bindValue( 
"$origPath{$this->properties['separationChar']}%" ) )
             ) );
         $s = $q->prepare();
         $s->execute();

Modified: trunk/TreeDatabaseTiein/src/backends/db_nested_set.php
==============================================================================
--- trunk/TreeDatabaseTiein/src/backends/db_nested_set.php [iso-8859-1] 
(original)
+++ trunk/TreeDatabaseTiein/src/backends/db_nested_set.php [iso-8859-1] Fri Oct 
 5 17:16:45 2007
@@ -374,7 +374,7 @@
         }
 
         // Delete all data for the deleted nodes
-        $nodeList = $this->fetchSubtree( $nodeId );
+        $nodeList = $this->fetchSubtreeDepthFirst( $nodeId );
         $this->store->deleteDataForNodes( $nodeList );
 
         // Fetch node information

Modified: trunk/TreeDatabaseTiein/tests/db_materialized_path_tree.php
==============================================================================
--- trunk/TreeDatabaseTiein/tests/db_materialized_path_tree.php [iso-8859-1] 
(original)
+++ trunk/TreeDatabaseTiein/tests/db_materialized_path_tree.php [iso-8859-1] 
Fri Oct  5 17:16:45 2007
@@ -73,6 +73,86 @@
         return $tree;
     }
 
+    public function testWithWrongSeparationChar()
+    {
+        $store = new ezcTreeDbExternalTableDataStore( $this->dbh, 'data', 
'id', 'data' );
+        $tree = new ezcTreeDbMaterializedPath(
+            $this->dbh,
+            'materialized_path',
+            $store,
+            '@'
+        );
+
+        $nodeList = $tree->fetchNodeById( 4 )->fetchSubtree();
+        self::assertSame( 1, $nodeList->size );
+    }
+
+    public function testCreateNodeWithInvalidId1()
+    {
+        $store = new ezcTreeDbExternalTableDataStore( $this->dbh, 'data', 
'id', 'data' );
+        $tree = new ezcTreeDbMaterializedPath(
+            $this->dbh,
+            'materialized_path',
+            $store,
+            '/'
+        );
+
+
+        try
+        {
+            $newNode = $tree->createNode( 'Is/This/Right', 'No' );
+            self::fail( 'Expected exception not thrown.' );
+        }
+        catch ( ezcTreeInvalidIdException $e )
+        {
+            self::assertSame( "The node ID 'Is/This/Right' contains the 
invalid character '/'.", $e->getMessage() );
+        }
+    }
+
+    public function testCreateNodeWithInvalidId2()
+    {
+        $store = new ezcTreeDbExternalTableDataStore( $this->dbh, 'data', 
'id', 'data' );
+        $tree = new ezcTreeDbMaterializedPath(
+            $this->dbh,
+            'materialized_path',
+            $store,
+            '@'
+        );
+
+        try
+        {
+            $newNode = $tree->createNode( '[EMAIL PROTECTED]@Right', 'No' );
+            self::fail( 'Expected exception not thrown.' );
+        }
+        catch ( ezcTreeInvalidIdException $e )
+        {
+            self::assertSame( "The node ID '[EMAIL PROTECTED]@Right' contains 
the invalid character '@'.", $e->getMessage() );
+        }
+    }
+
+    public function testSeparatingCharPropertyAccess()
+    {
+        $store = new ezcTreeDbExternalTableDataStore( $this->dbh, 'data', 
'id', 'data' );
+        $tree = new ezcTreeDbMaterializedPath(
+            $this->dbh,
+            'materialized_path',
+            $store,
+            '@'
+        );
+
+        self::assertSame( '@', $tree->separationChar );
+        self::assertSame( true, isset( $tree->separationChar ) );
+        try
+        {
+            $newNode = $tree->separationChar = '#';
+            self::fail( 'Expected exception not thrown.' );
+        }
+        catch ( ezcBasePropertyPermissionException $e )
+        {
+            self::assertSame( "The property 'separationChar' is read-only.", 
$e->getMessage() );
+        }
+    }
+
     public static function suite()
     {
          return new PHPUnit_Framework_TestSuite( 
"ezcTreeDbMaterializedPathTest" );

Modified: trunk/TreeDatabaseTiein/tests/suite.php
==============================================================================
--- trunk/TreeDatabaseTiein/tests/suite.php [iso-8859-1] (original)
+++ trunk/TreeDatabaseTiein/tests/suite.php [iso-8859-1] Fri Oct  5 17:16:45 
2007
@@ -13,6 +13,7 @@
  */
 require_once 'Tree/tests/tree.php';
 require_once 'db_materialized_path_tree.php';
+require_once 'db_materialized_path_tree_diff_separator.php';
 require_once 'db_nested_set_tree.php';
 require_once 'db_parent_child_tree.php';
 require_once 'copy_tree.php';
@@ -30,6 +31,7 @@
         $this->setName("TreeDatabaseTiein");
 
         $this->addTest( ezcTreeDbMaterializedPathTest::suite() );
+        $this->addTest( 
ezcTreeDbMaterializedPathTestWithDifferentSeparator::suite() );
         $this->addTest( ezcTreeDbNestedSetTest::suite() );
         $this->addTest( ezcTreeDbParentChildTest::suite() );
         $this->addTest( ezcTreeDbCopyTest::suite() );


-- 
svn-components mailing list
[email protected]
http://lists.ez.no/mailman/listinfo/svn-components

Reply via email to