PageRenderTime 61ms CodeModel.GetById 19ms RepoModel.GetById 0ms app.codeStats 0ms

/lib/ezc/Tree/src/backends/memory.php

https://bitbucket.org/crevillo/enetcall
PHP | 550 lines | 246 code | 54 blank | 250 comment | 12 complexity | 49977deb1f6de8b9a3ce238181c1facc MD5 | raw file
Possible License(s): GPL-2.0, BSD-3-Clause, LGPL-2.1
  1. <?php
  2. /**
  3. * File containing the ezcTreeMemory class.
  4. *
  5. * @copyright Copyright (C) 2005-2010 eZ Systems AS. All rights reserved.
  6. * @license http://ez.no/licenses/new_bsd New BSD License
  7. * @version //autogentag//
  8. * @filesource
  9. * @package Tree
  10. */
  11. /**
  12. * ezcTreeMemory is an implementation of a tree backend that operates on
  13. * an in-memory tree structure. Meta-information is kept in objects of the
  14. * ezcTreeMemoryNode class.
  15. *
  16. * Example:
  17. * <code>
  18. * <?php
  19. * // Create a new tree
  20. * $tree = ezcTreeMemory::create( new ezcTreeMemoryDataStore() );
  21. * // or
  22. * $tree = new ezcTreeMemory( new ezcTreeMemoryDataStore() );
  23. * ?>
  24. * </code>
  25. *
  26. * See {@link ezcTree} for examples on how to operate on the tree.
  27. *
  28. * @property-read ezcTreeXmlDataStore $store
  29. * The data store that is used for retrieving/storing data.
  30. * @property string $nodeClassName
  31. * Which class is used as tree node - this class *must* inherit
  32. * the ezcTreeNode class.
  33. *
  34. * @package Tree
  35. * @version //autogentag//
  36. * @mainclass
  37. */
  38. class ezcTreeMemory extends ezcTree
  39. {
  40. /**
  41. * Contains a list of all nodes, indexed by node ID that link directly to the create node so that they can be looked up quickly.
  42. *
  43. * @var array(string=>ezcTreeMemoryNode)
  44. */
  45. private $nodeList = array();
  46. /**
  47. * Contains the root node.
  48. *
  49. * @var ezcTreeMemoryNode
  50. */
  51. private $rootNode;
  52. /**
  53. * Stores the last auto generated ID that was used.
  54. *
  55. * @var integer $autoNodeId
  56. */
  57. private $autoNodeId = 0;
  58. /**
  59. * Constructs a new ezcTreeMemory object.
  60. *
  61. * The store that is used for data storage should be passed as the
  62. * $store argument.
  63. *
  64. * @param ezcTreeMemoryDataStore $store
  65. */
  66. protected function __construct( ezcTreeMemoryDataStore $store )
  67. {
  68. $this->properties['store'] = $store;
  69. $this->properties['autoId'] = false;
  70. }
  71. /**
  72. * This method generates the next node ID.
  73. *
  74. * @return integer
  75. */
  76. protected function generateNodeID()
  77. {
  78. $this->autoNodeId++;
  79. return $this->autoNodeId;
  80. }
  81. /**
  82. * A factory method that creates a new empty tree using the data store $store.
  83. *
  84. * @param ezcTreeMemoryDataStore $store
  85. * @return ezcTreeMemory
  86. */
  87. public static function create( ezcTreeMemoryDataStore $store )
  88. {
  89. $newTree = new ezcTreeMemory( $store );
  90. $newTree->nodeList = null;
  91. $newTree->rootNode = null;
  92. return $newTree;
  93. }
  94. /**
  95. * Returns whether the node with ID $nodeId exists.
  96. *
  97. * @param string $nodeId
  98. * @return bool
  99. */
  100. public function nodeExists( $nodeId )
  101. {
  102. return isset( $this->nodeList[$nodeId] );
  103. }
  104. /**
  105. * Returns the node identified by the ID $nodeId.
  106. *
  107. * @param string $nodeId
  108. * @throws ezcTreeInvalidIdException if there is no node with ID $nodeId
  109. * @return ezcTreeNode
  110. */
  111. public function fetchNodeById( $nodeId )
  112. {
  113. return $this->getNodeById( $nodeId )->node;
  114. }
  115. /**
  116. * Returns the node container for node $nodeId.
  117. *
  118. * @param string $nodeId
  119. * @throws ezcTreeInvalidIdException if there is no node with ID $nodeId
  120. * @return ezcTreeMemoryNode
  121. */
  122. private function getNodeById( $nodeId )
  123. {
  124. if ( !$this->nodeExists( $nodeId ) )
  125. {
  126. throw new ezcTreeUnknownIdException( $nodeId );
  127. }
  128. return $this->nodeList[$nodeId];
  129. }
  130. /**
  131. * Returns all the children of the node with ID $nodeId.
  132. *
  133. * @param string $nodeId
  134. * @return ezcTreeNodeList
  135. */
  136. public function fetchChildren( $nodeId )
  137. {
  138. $treeNode = $this->getNodeById( $nodeId );
  139. $list = new ezcTreeNodeList;
  140. foreach ( $treeNode->children as $nodeId => $child )
  141. {
  142. $list->addNode( $child->node );
  143. }
  144. return $list;
  145. }
  146. /**
  147. * Returns the parent node of the node with ID $nodeId.
  148. *
  149. * This method returns null if there is no parent node.
  150. *
  151. * @param string $nodeId
  152. * @return ezcTreeNode
  153. */
  154. public function fetchParent( $nodeId )
  155. {
  156. $treeNode = $this->getNodeById( $nodeId );
  157. $parentNode = $treeNode->parent;
  158. return $parentNode !== null ? $parentNode->node : null;
  159. }
  160. /**
  161. * Returns all the nodes in the path from the root node to the node with ID
  162. * $nodeId, including those two nodes.
  163. *
  164. * @param string $nodeId
  165. * @return ezcTreeNodeList
  166. */
  167. public function fetchPath( $nodeId )
  168. {
  169. $list = new ezcTreeNodeList;
  170. $memoryNode = $this->getNodeById( $nodeId );
  171. $nodes = array();
  172. $nodes[] = $memoryNode->node;
  173. $memoryNode = $memoryNode->parent;
  174. while ( $memoryNode !== null )
  175. {
  176. $nodes[] = $memoryNode->node;
  177. $memoryNode = $memoryNode->parent;
  178. }
  179. $list = new ezcTreeNodeList;
  180. foreach ( array_reverse( $nodes ) as $node )
  181. {
  182. $list->addNode( $node );
  183. }
  184. return $list;
  185. }
  186. /**
  187. * Adds the children nodes of the node $memoryNode to the
  188. * ezcTreeNodeList $list.
  189. *
  190. * @param ezcTreeNodeList $list
  191. * @param ezcTreeMemoryNode $memoryNode
  192. */
  193. private function addChildNodesDepthFirst( ezcTreeNodeList $list, ezcTreeMemoryNode $memoryNode )
  194. {
  195. foreach ( $memoryNode->children as $nodeId => $childMemoryNode )
  196. {
  197. $list->addNode( $childMemoryNode->node );
  198. $this->addChildNodesDepthFirst( $list, $childMemoryNode );
  199. }
  200. }
  201. /**
  202. * Returns the node with ID $nodeId and all its children, sorted accoring to
  203. * the {@link http://en.wikipedia.org/wiki/Depth-first_search Depthth-first sorting}
  204. * algorithm.
  205. *
  206. * @param string $nodeId
  207. * @return ezcTreeNodeList
  208. */
  209. public function fetchSubtreeDepthFirst( $nodeId )
  210. {
  211. $list = new ezcTreeNodeList;
  212. $memoryNode = $this->getNodeById( $nodeId );
  213. $list->addNode( $memoryNode->node );
  214. $this->addChildNodesDepthFirst( $list, $memoryNode );
  215. return $list;
  216. }
  217. /**
  218. * Alias for fetchSubtreeDepthFirst().
  219. *
  220. * @param string $nodeId
  221. * @return ezcTreeNodeList
  222. */
  223. public function fetchSubtree( $nodeId )
  224. {
  225. return $this->fetchSubtreeDepthFirst( $nodeId );
  226. }
  227. /**
  228. * Adds the children nodes of the node $memoryNode to the
  229. * ezcTreeNodeList $list.
  230. *
  231. * @param ezcTreeNodeList $list
  232. * @param ezcTreeMemoryNode $memoryNode
  233. */
  234. private function addChildNodesBreadthFirst( ezcTreeNodeList $list, ezcTreeMemoryNode $memoryNode )
  235. {
  236. foreach ( $memoryNode->children as $nodeId => $childMemoryNode )
  237. {
  238. $list->addNode( $childMemoryNode->node );
  239. }
  240. foreach ( $memoryNode->children as $nodeId => $childMemoryNode )
  241. {
  242. $this->addChildNodesBreadthFirst( $list, $childMemoryNode );
  243. }
  244. }
  245. /**
  246. * Returns the node with ID $nodeId and all its children, sorted according to
  247. * the {@link http://en.wikipedia.org/wiki/Breadth-first_search Breadth-first sorting}
  248. * algorithm.
  249. *
  250. * @param string $nodeId
  251. * @return ezcTreeNodeList
  252. */
  253. public function fetchSubtreeBreadthFirst( $nodeId )
  254. {
  255. $list = new ezcTreeNodeList;
  256. $memoryNode = $this->getNodeById( $nodeId );
  257. $list->addNode( $memoryNode->node );
  258. $this->addChildNodesBreadthFirst( $list, $memoryNode );
  259. return $list;
  260. }
  261. /**
  262. * Returns the number of direct children of the node with ID $nodeId.
  263. *
  264. * @param string $nodeId
  265. * @return int
  266. */
  267. public function getChildCount( $nodeId )
  268. {
  269. return count( $this->getNodeById( $nodeId )->children );
  270. }
  271. /**
  272. * Helper method that iterates recursively over the children of $node to
  273. * count the number of children.
  274. *
  275. * @param integer $count
  276. * @param ezcTreeMemoryNode $node
  277. */
  278. private function countChildNodes( &$count, ezcTreeMemoryNode $node )
  279. {
  280. foreach ( $node->children as $nodeId => $node )
  281. {
  282. $count++;
  283. $this->countChildNodes( $count, $node );
  284. }
  285. }
  286. /**
  287. * Returns the number of children of the node with ID $nodeId, recursively
  288. *
  289. * @param string $nodeId
  290. * @return int
  291. */
  292. public function getChildCountRecursive( $nodeId )
  293. {
  294. $count = 0;
  295. $node = $this->getNodeById( $nodeId );
  296. $this->countChildNodes( $count, $node );
  297. return $count;
  298. }
  299. /**
  300. * Returns the distance from the root node to the node with ID $nodeId
  301. *
  302. * @param string $nodeId
  303. * @return int
  304. */
  305. public function getPathLength( $nodeId )
  306. {
  307. $childNode = $this->getNodeById( $nodeId );
  308. $length = -1;
  309. while ( $childNode !== null )
  310. {
  311. $childNode = $childNode->parent;
  312. $length++;
  313. }
  314. return $length;
  315. }
  316. /**
  317. * Returns whether the node with ID $nodeId has children
  318. *
  319. * @param string $nodeId
  320. * @return bool
  321. */
  322. public function hasChildNodes( $nodeId )
  323. {
  324. return count( $this->getNodeById( $nodeId )->children ) > 0;
  325. }
  326. /**
  327. * Returns whether the node with ID $childId is a direct child of the node
  328. * with ID $parentId
  329. *
  330. * @param string $childId
  331. * @param string $parentId
  332. * @return bool
  333. */
  334. public function isChildOf( $childId, $parentId )
  335. {
  336. $childNode = $this->getNodeById( $childId );
  337. $parentNode = $this->getNodeById( $parentId );
  338. if ( $childNode->parent->node === $parentNode->node )
  339. {
  340. return true;
  341. }
  342. return false;
  343. }
  344. /**
  345. * Returns whether the node with ID $childId is a direct or indirect child
  346. * of the node with ID $parentId
  347. *
  348. * @param string $childId
  349. * @param string $parentId
  350. * @return bool
  351. */
  352. public function isDescendantOf( $childId, $parentId )
  353. {
  354. $childNode = $this->getNodeById( $childId );
  355. $parentNode = $this->getNodeById( $parentId );
  356. if ( $childNode === $parentNode )
  357. {
  358. return false;
  359. }
  360. while ( $childNode !== null )
  361. {
  362. if ( $childNode->node === $parentNode->node )
  363. {
  364. return true;
  365. }
  366. $childNode = $childNode->parent;
  367. }
  368. return false;
  369. }
  370. /**
  371. * Returns whether the nodes with IDs $child1Id and $child2Id are siblings
  372. * (ie, the share the same parent)
  373. *
  374. * @param string $child1Id
  375. * @param string $child2Id
  376. * @return bool
  377. */
  378. public function isSiblingOf( $child1Id, $child2Id )
  379. {
  380. $elem1 = $this->getNodeById( $child1Id );
  381. $elem2 = $this->getNodeById( $child2Id );
  382. return (
  383. ( $child1Id !== $child2Id ) &&
  384. ( $elem1->parent === $elem2->parent )
  385. );
  386. }
  387. /**
  388. * Sets a new node as root node, this wipes also out the whole tree
  389. *
  390. * @param ezcTreeNode $node
  391. */
  392. public function setRootNode( ezcTreeNode $node )
  393. {
  394. // wipe nodelist and data
  395. $this->nodeList = array();
  396. $this->store->deleteDataForAllNodes();
  397. // replace root node
  398. $newObj = new ezcTreeMemoryNode( $node, array() );
  399. $this->rootNode = $newObj;
  400. // Add to node list
  401. $this->nodeList[$node->id] = $newObj;
  402. }
  403. /**
  404. * Returns the root node
  405. *
  406. * This methods returns null if there is no root node.
  407. *
  408. * @return ezcTreeNode
  409. */
  410. public function getRootNode()
  411. {
  412. if ( $this->rootNode )
  413. {
  414. return $this->rootNode->node;
  415. }
  416. return null;
  417. }
  418. /**
  419. * Adds the node $childNode as child of the node with ID $parentId
  420. *
  421. * @param string $parentId
  422. * @param ezcTreeNode $childNode
  423. */
  424. public function addChild( $parentId, ezcTreeNode $childNode )
  425. {
  426. if ( $this->inTransaction )
  427. {
  428. $this->addTransactionItem( new ezcTreeTransactionItem( ezcTreeTransactionItem::ADD, $childNode, null, $parentId ) );
  429. return;
  430. }
  431. // Locate parent node
  432. $parentMemoryNode = $this->getNodeById( $parentId );
  433. // Create new node
  434. $newObj = new ezcTreeMemoryNode( $childNode, array(), $parentMemoryNode );
  435. // Append to parent node
  436. $parentMemoryNode->children[$childNode->id] = $newObj;
  437. // Add to node list
  438. $this->nodeList[$childNode->id] = $newObj;
  439. }
  440. /**
  441. * Deletes the node with ID $nodeId from the tree, including all its children
  442. *
  443. * @param string $nodeId
  444. */
  445. public function delete( $nodeId )
  446. {
  447. if ( $this->inTransaction )
  448. {
  449. $this->addTransactionItem( new ezcTreeTransactionItem( ezcTreeTransactionItem::DELETE, null, $nodeId ) );
  450. return;
  451. }
  452. // locate node to move
  453. $nodeToDelete = $this->getNodeById( $nodeId );
  454. // fetch the whole subtree and delete all the associated data
  455. $children = $nodeToDelete->node->fetchSubtree();
  456. $this->store->deleteDataForNodes( $children );
  457. // Use the parent to remove the child
  458. unset( $nodeToDelete->parent->children[$nodeId] );
  459. // Remove the node and all its children
  460. foreach ( new ezcTreeNodeListIterator( $this, $children ) as $nodeId => $data )
  461. {
  462. unset( $this->nodeList[$nodeId] );
  463. }
  464. }
  465. /**
  466. * Moves the node with ID $nodeId as child to the node with ID $targetParentId
  467. *
  468. * @param string $nodeId
  469. * @param string $targetParentId
  470. */
  471. public function move( $nodeId, $targetParentId )
  472. {
  473. if ( $this->inTransaction )
  474. {
  475. $this->addTransactionItem( new ezcTreeTransactionItem( ezcTreeTransactionItem::MOVE, null, $nodeId, $targetParentId ) );
  476. return;
  477. }
  478. // locate node to move
  479. $nodeToMove = $this->getNodeById( $nodeId );
  480. // locate new parent
  481. $newParent = $this->getNodeById( $targetParentId );
  482. // new placement for node
  483. $newParent->children[$nodeId] = $nodeToMove;
  484. // remove old location from previous parent
  485. unset( $nodeToMove->parent->children[$nodeId] );
  486. // update parent attribute of the node
  487. $nodeToMove->parent = $newParent;
  488. }
  489. /**
  490. * Fixates the transaction.
  491. */
  492. public function fixateTransaction()
  493. {
  494. }
  495. }
  496. ?>