Memory.php 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353
  1. <?php
  2. //
  3. // +----------------------------------------------------------------------+
  4. // | PHP Version 4 |
  5. // +----------------------------------------------------------------------+
  6. // | Copyright (c) 1997-2003 The PHP Group |
  7. // +----------------------------------------------------------------------+
  8. // | This source file is subject to version 2.02 of the PHP license, |
  9. // | that is bundled with this package in the file LICENSE, and is |
  10. // | available at through the world-wide-web at |
  11. // | http://www.php.net/license/2_02.txt. |
  12. // | If you did not receive a copy of the PHP license and are unable to |
  13. // | obtain it through the world-wide-web, please send a note to |
  14. // | license@php.net so we can mail you a copy immediately. |
  15. // +----------------------------------------------------------------------+
  16. // | Authors: Wolfram Kriesing <wolfram@kriesing.de> |
  17. // +----------------------------------------------------------------------+
  18. //
  19. // $Id: Memory.php,v 1.20 2003/02/26 18:46:31 cain Exp $
  20. require_once 'Tree/Common.php';
  21. require_once 'Tree/Error.php';
  22. /**
  23. * this class can be used to step through a tree using ['parent'], ['child'], etc.
  24. * the tree is saved as flat data in a db, where at least the parent
  25. * needs to be given if a previous member is given too then the order
  26. * on a level can be determined too
  27. * actually this class was used for a navigation tree
  28. * now it is extended to serve any kind of tree
  29. * you can unambigiously refer to any element by using the following
  30. * syntax
  31. * tree->data[currentId][<where>]...[<where>]
  32. * <where> can be either "parent", "child", "next" or "previous", this way
  33. * you can "walk" from any point to any other point in the tree
  34. * by using <where> in any order you want
  35. * example (in parentheses the id):
  36. * root
  37. * +---level 1_1 (1)
  38. * | +----level 2_1 (2)
  39. * | +----level 2_2 (3)
  40. * | +-----level 3_1 (4)
  41. * +---level 1_2 (5)
  42. *
  43. * the database table to this structure (without defined order)
  44. * id parentId name
  45. * 1 0 level 1_1
  46. * 2 1 level 2_1
  47. * 3 1 level 2_1
  48. * 4 3 level 3_1
  49. * 5 0 level 1_2
  50. *
  51. * now you can refer to elements for example like this (all examples assume you know the structure):
  52. * to go from "level 3_1" to "level 1_1": $tree->data[4]['parent']['parent']
  53. * to go from "level 3_1" to "level 1_2": $tree->data[4]['parent']['parent']['next']
  54. * to go from "level 2_1" to "level 3_1": $tree->data[2]['next']['child']
  55. * to go from "level 2_2" to "level 2_1": $tree->data[3]['previous']
  56. * to go from "level 1_2" to "level 3_1": $tree->data[5]['previous']['child']['next']['child']
  57. *
  58. on a pentium 1.9 GHz 512 MB RAM, Linux 2.4, Apache 1.3.19, PHP 4.0.6
  59. performance statistics for version 1.26, using examples/Tree/Tree.php
  60. reading from DB and preparing took: 0.14958894252777
  61. building took: 0.074488043785095
  62. buildStructure took: 0.05151903629303
  63. setting up the tree time: 0.29579293727875
  64. number of elements: 1564
  65. deepest level: 17
  66. so you can use it for tiny-big trees too :-)
  67. but watch the db traffic, which might be considerable, depending on your setup
  68. FIXXXME there is one really bad thing about the entire class, at some points there are references to
  69. $this->data returned, or the programmer can even access this->data, which means he can change the
  70. structure, since this->data can not be set to read-only, therefore this->data has to be handled with great care
  71. !!! never do something like this: $x = &$tree->data[<some-id>]; $x = $y; this overwrites the element in the structure !!!
  72. *
  73. *
  74. * @access public
  75. * @author Wolfram Kriesing <wolfram@kriesing.de>
  76. * @version 2001/06/27
  77. * @package Tree
  78. */
  79. class Tree_Memory extends Tree_Common
  80. {
  81. /**
  82. * this array contains the pure data from the DB
  83. * which are always kept, since all other structures will
  84. * only make references on any element
  85. * and those data are extended by the elements 'parent' 'children' etc...
  86. * @var array $data
  87. */
  88. var $data = array();
  89. /**
  90. * this array contains references to this->data but it
  91. * additionally represents the directory structure
  92. * that means the array has as many dimensions as the
  93. * tree structure has levels
  94. * but this array is only used internally from outside you can do everything using
  95. * the node-id's
  96. *
  97. * @var array $structure
  98. * @access private
  99. */
  100. var $structure = array();
  101. /**
  102. * it contains all the parents and their children, where the parentId is the
  103. * key and all the children are the values, this is for speeding up the tree-building process
  104. *
  105. * @var array $children
  106. */
  107. var $children = array();
  108. /**
  109. * @access private
  110. * @var boolean saves if tree nodes shall be removed recursively
  111. * @see setRemoveRecursively()
  112. */
  113. var $removeRecursively = false;
  114. /**
  115. * @access public
  116. * @var integer $debug the debug mode, if > 0 then debug info are shown,
  117. * actually those messages only show performance times
  118. */
  119. var $debug = 0;
  120. /**
  121. * @see &getNode()
  122. * @see &_getNode()
  123. * @access private
  124. * @var integer $_getNodeMaxLevel variable only used in the method getNode and _getNode
  125. */
  126. var $_getNodeMaxLevel;
  127. /**
  128. * @see &getNode()
  129. * @see &_getNode()
  130. * @access private
  131. * @var integer $_getNodeCurParent variable only used in the method getNode and _getNode
  132. */
  133. var $_getNodeCurParent;
  134. /**
  135. * the maximum depth of the tree
  136. * @access private
  137. * @var int the maximum depth of the tree
  138. */
  139. var $_treeDepth = 0;
  140. /**
  141. * set up this object
  142. *
  143. * @version 2001/06/27
  144. * @access public
  145. * @author Wolfram Kriesing <wolfram@kriesing.de>
  146. * @param mixed $dsn this is a DSN for the PEAR::DB, can be either an object/string
  147. * @param array $options additional options you can set
  148. */
  149. function Tree_Memory( $type , $dsn='' , $options=array() )
  150. {
  151. $this->Tree_Options($options); // set the options for $this
  152. require_once("Tree/Memory/$type.php");
  153. $className = 'Tree_Memory_'.$type;
  154. $this->dataSourceClass =& new $className( $dsn , $options );
  155. // copy the options to be able to get them via getOption(s)
  156. //FIXXME this is not really cool, maybe overwrite the *Option* methods!!!
  157. if( isset($this->dataSourceClass->options) )
  158. $this->options = $this->dataSourceClass->options;
  159. } // end of function
  160. /**
  161. * use this to switch data sources on the run
  162. * i.e. if you are reading the data from a db-tree and want to save it
  163. * as xml data (which will work one day too)
  164. * or reading the data from an xml file and writing it in the db
  165. * which should already work
  166. *
  167. * @version 2002/01/17
  168. * @access public
  169. * @author Wolfram Kriesing <wolfram@kriesing.de>
  170. * @param string $dsn this is a DSN of the for that PEAR::DB uses it
  171. * only that additionally you can add parameters like ...?table=test_table
  172. * to define the table it shall work on
  173. * @param array $options additional options you can set
  174. * @return boolean true on success
  175. */
  176. function switchDataSource( $type , $dsn='' , $options=array() )
  177. {
  178. $data = $this->getNode();
  179. //$this->Tree( $dsn , $options );
  180. $this->Tree_Memory( $type , $GLOBALS['dummy'] , $options );
  181. // this method prepares data retreived using getNode to be used
  182. // in this type of tree
  183. $this->dataSourceClass->setData($data);
  184. $this->setup();
  185. }
  186. /**
  187. *
  188. *
  189. * @version 2002/01/19
  190. * @access public
  191. * @author Wolfram Kriesing <wolfram@kriesing.de>
  192. * @return
  193. */
  194. function setupByRawData( $string )
  195. {
  196. // expects
  197. // for XML an XML-String,
  198. // for DB-a result set, may be or an array, dont know here - not implemented yet
  199. $res = $this->dataSourceClass->setupByRawData( $string );
  200. return $this->_setup( $res );
  201. }
  202. /**
  203. *
  204. *
  205. * @version 2002/01/19
  206. * @access public
  207. * @author Wolfram Kriesing <wolfram@kriesing.de>
  208. * @param array the result of a query which retreives (all) the tree data from a source
  209. * @return true or Tree_Error
  210. */
  211. function setup($data=null)
  212. {
  213. if( $this->debug )
  214. {
  215. $startTime = split(" ",microtime());
  216. $startTime = $startTime[1]+$startTime[0];
  217. }
  218. if(PEAR::isError($res = $this->dataSourceClass->setup($data)) )
  219. return $res;
  220. if( $this->debug )
  221. {
  222. $endTime = split(" ",microtime());
  223. $endTime = $endTime[1]+$endTime[0];
  224. print( " reading and preparing tree data took: ".($endTime - $startTime)." <br>" );
  225. }
  226. return $this->_setup( $res );
  227. }
  228. /**
  229. * retreive all the navigation data from the db and build the
  230. * tree in the array data and structure
  231. *
  232. * @version 2001/11/20
  233. * @access private
  234. * @author Wolfram Kriesing <wolfram@kriesing.de>
  235. * @return boolean true on success
  236. */
  237. function _setup( $setupData )
  238. {
  239. // TODO sort by prevId (parentId,prevId $addQuery) too if it exists in the table, or the root might be wrong
  240. // TODO since the prevId of the root should be 0
  241. if( !$setupData )
  242. return false;
  243. //FIXXXXXME validate the structure. i.e. a problem occurs, if you give one node, which has a parentId=1 it screws up everything!!!
  244. // empty the data structures, since we are reading the data from the db (again)
  245. $this->structure = array();
  246. $this->data = array();
  247. $this->children = array();
  248. // build an array where all the parents have their children as a member
  249. // this i do to speed up the buildStructure
  250. foreach( $setupData as $values )
  251. {
  252. if( is_array($values) )
  253. {
  254. $this->data[$values['id']] = $values;
  255. $this->children[ $values['parentId'] ][] = $values['id'];
  256. }
  257. }
  258. // walk through all the children on each level and set the next/previous relations
  259. // of those children, since all children for "children[$id]" are on the same level we can do
  260. // this here :-)
  261. foreach( $this->children as $children )
  262. {
  263. $lastPrevId = 0;
  264. if(sizeof($children))
  265. foreach( $children as $key )
  266. {
  267. if( $lastPrevId )
  268. {
  269. $this->data[$lastPrevId]['nextId'] = $key; // remember the nextId too, so the build process can be sped up
  270. $this->data[$lastPrevId]['next'] = &$this->data[$key];
  271. $this->data[$key]['prevId'] = $lastPrevId;
  272. $this->data[$key]['previous'] = &$this->data[ $lastPrevId ];
  273. }
  274. $lastPrevId = $key;
  275. }
  276. }
  277. //print_r($this->children);
  278. if( $this->debug )
  279. {
  280. $startTime = split(" ",microtime());
  281. $startTime = $startTime[1]+$startTime[0];
  282. }
  283. // when NO prevId is given, sort the entries in each level by the given sort order (to be defined)
  284. // and set the prevId so the build can work properly
  285. if( !isset($setupData[0]['prevId']) ) // does a prevId exist?
  286. {
  287. $lastPrevId = 0;
  288. $lastParentId = 0;
  289. $level = 0;
  290. // build the entire recursive relations, so you have 'parentId', 'childId', 'nextId', 'prevId'
  291. // and the references 'child', 'parent', 'next', 'previous' set in the property 'data'
  292. foreach( $this->data as $key=>$value )
  293. {
  294. // most if checks in this foreach are for the following reason, if not stated otherwise:
  295. // dont make an data[''] or data[0] since this was not read from the DB, because id is autoincrement and starts at 1
  296. // and also in an xml tree there can not be an element </> , i hope :-)
  297. if( $value['parentId'] ) // see comment above
  298. {
  299. $this->data[$key]['parent'] = &$this->data[ $value['parentId'] ];
  300. // the parent has an extra array which contains a reference to all it's children, set it here
  301. $this->data[ $value['parentId'] ]['children'][] = &$this->data[$key];
  302. }
  303. // was a child saved (in the above 'if')
  304. if( isset($this->children[$key]) && sizeof( $this->children[$key] ) ) // see comment above
  305. {
  306. // refer to the first child in the [child] and [childId] keys
  307. $this->data[$key]['childId'] = $this->children[$key][0];
  308. $this->data[$key]['child'] = &$this->data[ $this->children[$key][0] ];
  309. }
  310. $lastParentId = $value['parentId'];
  311. }
  312. }
  313. if( $this->debug )
  314. {
  315. $endTime = split(" ",microtime());
  316. $endTime = $endTime[1]+$endTime[0];
  317. print( " building took: ".($endTime - $startTime)." <br>" );
  318. }
  319. // build the property 'structure'
  320. $this->structure = array(); // empty it, just to be sure everything will be set properly
  321. if( $this->debug )
  322. {
  323. $startTime = split(" ",microtime());
  324. $startTime = $startTime[1]+$startTime[0];
  325. }
  326. // build all the children that are on the root level, if we wouldnt do that
  327. // we would have to create a root element with an id 0, but since this is not
  328. // read from the db we dont add another element, the user wants to get what he had saved
  329. if( sizeof($this->children[0]) )
  330. foreach( $this->children[0] as $rootElement )
  331. {
  332. $this->buildStructure( $rootElement , $this->structure );
  333. }
  334. if( $this->debug )
  335. {
  336. $endTime = split(" ",microtime());
  337. $endTime = $endTime[1]+$endTime[0];
  338. print( " buildStructure took: ".($endTime - $startTime)." <br>" );
  339. }
  340. return true;
  341. }
  342. /**
  343. * adds _one_ new element in the tree under the given parent
  344. * the values' keys given have to match the db-columns, because the
  345. * value gets inserted in the db directly
  346. * to add an entire node containing children and so on see 'addNode()'
  347. * @see addNode()
  348. * @version 2001/10/09
  349. * @access public
  350. * @author Wolfram Kriesing <wolfram@kriesing.de>
  351. * @param array $newValues this array contains the values that shall be inserted in the db-table
  352. * @param int the parent id
  353. * @param int the prevId
  354. * @return mixed either boolean false on failure or the id of the inserted row
  355. */
  356. function add( $newValues , $parentId=0 , $prevId=0 )
  357. {
  358. // see comments in 'move' and 'remove'
  359. if (method_exists($this->dataSourceClass,'add')) {
  360. return $this->dataSourceClass->add( $newValues , $parentId , $prevId );
  361. } else {
  362. return $this->_throwError( 'method not implemented yet.' , __LINE__ );
  363. }
  364. } // end of function
  365. /**
  366. * removes the given node and all children if removeRecursively is on
  367. *
  368. * @version 2002/01/24
  369. * @access public
  370. * @author Wolfram Kriesing <wolfram@kriesing.de>
  371. * @param mixed $id the id of the node to be removed
  372. * @return boolean true on success
  373. */
  374. function remove( $id )
  375. {
  376. // if removing recursively is not allowed, which means every child should be removed
  377. // then check if this element has a child and return "sorry baby cant remove :-) "
  378. if ($this->removeRecursively != true) {
  379. if (isset( $this->data[$id]['child'] )) {
  380. // TODO raise PEAR warning
  381. return $this->_throwError("Element with id=$id has children, cant be removed. Set 'setRemoveRecursively' to true to allow this.",__LINE__);
  382. }
  383. }
  384. // see comment in 'move'
  385. // if the prevId is in use we need to update the prevId of the element after the one that
  386. // is removed too, to have the prevId of the one that is removed!!!
  387. if (method_exists($this->dataSourceClass,'remove')) {
  388. return $this->dataSourceClass->remove( $id );
  389. } else {
  390. return $this->_throwError( 'method not implemented yet.' , __LINE__ );
  391. }
  392. }
  393. /**
  394. * collects the ID's of the elements to be removed
  395. *
  396. * @version 2001/10/09
  397. * @access public
  398. * @author Wolfram Kriesing <wolfram@kriesing.de>
  399. * @param mixed $id the id of the node to be removed
  400. * @return boolean true on success
  401. */
  402. function _remove( $element )
  403. {
  404. return $element['id'];
  405. } // end of function
  406. /**
  407. * move an entry under a given parent or behind a given entry.
  408. * !!! the 'move behind another element' is only implemented for nested trees now!!!
  409. * If a newPrevId is given the newParentId is dismissed!
  410. * call it either like this:
  411. * $tree->move( x , y )
  412. * to move the element (or entire tree) with the id x
  413. * under the element with the id y
  414. * or
  415. * $tree->move( x , 0 , y ); // ommit the second parameter by setting it to 0
  416. * to move the element (or entire tree) with the id x
  417. * behind the element with the id y
  418. * or
  419. * $tree->move( array(x1,x2,x3) , ...
  420. * the first parameter can also be an array of elements that shall be moved
  421. * the second and third para can be as described above
  422. *
  423. * @version 2002/06/08
  424. * @access public
  425. * @author Wolfram Kriesing <wolfram@kriesing.de>
  426. * @param integer the id(s) of the element(s) that shall be moved
  427. * @param integer the id of the element which will be the new parent
  428. * @param integer if prevId is given the element with the id idToMove
  429. * shall be moved _behind_ the element with id=prevId
  430. * if it is 0 it will be put at the beginning
  431. * @return boolean true for success
  432. */
  433. function move( $idsToMove , $newParentId , $newPrevId=0 )
  434. {
  435. settype($idsToMove,'array');
  436. $errors = array();
  437. foreach( $idsToMove as $idToMove )
  438. {
  439. $ret = $this->_move( $idToMove , $newParentId , $newPrevId );
  440. if( PEAR::isError($ret) )
  441. $errors[] = $ret;
  442. }
  443. // FIXXME return a Tree_Error, not an array !!!!!
  444. if( sizeof($errors) )
  445. return $errors;
  446. return true;
  447. }
  448. /**
  449. * this method moves one tree element
  450. *
  451. * @see move()
  452. * @version 2001/10/10
  453. * @access public
  454. * @author Wolfram Kriesing <wolfram@kriesing.de>
  455. * @param integer the id of the element that shall be moved
  456. * @param integer the id of the element which will be the new parent
  457. * @param integer if prevId is given the element with the id idToMove
  458. * shall be moved _behind_ the element with id=prevId
  459. * if it is 0 it will be put at the beginning
  460. * @return mixed true for success, Tree_Error on failure
  461. */
  462. function _move( $idToMove , $newParentId , $prevId=0 )
  463. {
  464. if( $idToMove == $newParentId ) // itself can not be a parent of itself
  465. // TODO PEAR-ize error
  466. return TREE_ERROR_INVALID_PARENT;
  467. // check if $newParentId is a child (or a child-child ...) of $idToMove
  468. // if so prevent moving, because that is not possible
  469. // if( @$this->data[$idToMove]['children'] ) // does this element have children?
  470. if( $this->hasChildren($idToMove) ) // does this element have children?
  471. {
  472. // $allChildren = $this->data[$idToMove]['children'];
  473. $allChildren = $this->getChildren($idToMove);
  474. // FIXXME what happens here we are changing $allChildren, doesnt this change the
  475. // property data too??? since getChildren (might, not yet) return a reference
  476. while (list(, $aChild) = each ($allChildren)) // use while since foreach only works on a copy of the data to loop through, but we are changing $allChildren in the loop
  477. {
  478. array_shift( $allChildren ); // remove the first element because if array_merge is called the array pointer seems to be
  479. // set to the beginning and this way the beginning is always the current element, simply work off and truncate in front
  480. if( @$aChild['children'] )
  481. {
  482. $allChildren = array_merge( $allChildren , $aChild['children'] );
  483. }
  484. if( $newParentId == $aChild['id'] )
  485. // TODO PEAR-ize error
  486. return TREE_ERROR_INVALID_PARENT;
  487. }
  488. }
  489. // what happens if i am using the prevId too, then the db class also
  490. // needs to know where the element should be moved to
  491. // and it has to change the prevId of the element that will be after it
  492. // so we may be simply call some method like 'update' too?
  493. if( method_exists($this->dataSourceClass,'move') )
  494. return $this->dataSourceClass->move( $idToMove , $newParentId , $prevId );
  495. else
  496. return $this->_throwError( 'method not implemented yet.' , __LINE__ );
  497. } // end of function
  498. /**
  499. * update data in a node
  500. *
  501. * @version 2002/01/29
  502. * @access public
  503. * @author Wolfram Kriesing <wolfram@kriesing.de>
  504. * @param array $data the data to update
  505. * @return
  506. */
  507. function update( $id , $data )
  508. {
  509. if (method_exists($this->dataSourceClass,'update')) {
  510. return $this->dataSourceClass->update($id,$data);
  511. } else {
  512. return $this->_throwError( 'method not implemented yet.' , __LINE__ );
  513. }
  514. } // end of function
  515. //
  516. //
  517. // from here all methods are not interacting on the 'dataSourceClass'
  518. //
  519. //
  520. /**
  521. * builds the structure in the parameter $insertIn
  522. * this function works recursively down into depth of the folder structure
  523. * it builds an array which goes as deep as the structure goes
  524. *
  525. * @access public
  526. * @version 2001/05/02
  527. * @author Wolfram Kriesing <wolfram@kriesing.de>
  528. * @param integer $parentId the parent for which it's structure shall be built
  529. * @param integer $insertIn the array where to build the structure in
  530. * given as a reference to be sure the substructure is built
  531. * in the same array as passed to the function
  532. * @return boolean returns always true
  533. *
  534. */
  535. function buildStructure( $parentId , &$insertIn )
  536. {
  537. // create the element, so it exists in the property "structure"
  538. // also if there are no children below
  539. $insertIn[$parentId] = array();
  540. // set the level, since we are walking through the structure here anyway we
  541. // can do this here, instead of up in the setup method :-)
  542. // always set the level to one higher than the parent's level, easy ha?
  543. if (isset($this->data[$parentId]['parent']['level'])) { // this applies only to the root element(s)
  544. $this->data[$parentId]['level'] = $this->data[$parentId]['parent']['level']+1;
  545. if ($this->data[$parentId]['level']>$this->_treeDepth) {
  546. $this->_treeDepth = $this->data[$parentId]['level'];
  547. }
  548. } else {
  549. $this->data[$parentId]['level'] = 0; // set first level number to 0
  550. }
  551. if (isset($this->children[$parentId]) && sizeof($this->children[$parentId])) {
  552. // go thru all the folders
  553. foreach ($this->children[$parentId] as $child) {
  554. // build the structure under this folder,
  555. // use the current folder as the new parent and call build recursively
  556. // to build all the children
  557. // by calling build with $insertIn[someindex] the array is filled
  558. // since the array was empty before
  559. $this->buildStructure( $child , $insertIn[$parentId] );
  560. }
  561. }
  562. return true;
  563. } // end of function
  564. /**
  565. * this method only serves to call the _walk method and reset $this->walkReturn
  566. * that will be returned by all the walk-steps
  567. *
  568. * @version 2001/11/25
  569. * @access public
  570. * @author Wolfram Kriesing <wolfram@kriesing.de>
  571. * @param mixed $walkFunction the name of the function to call for each walk step,
  572. * or an array for a method, where
  573. * [0] is the method name and [1] the object
  574. * @param array $id the id to start walking through the tree, everything below is walked through
  575. * @param string $returnType the return of all the walk data will be of the given type (values: string, array)
  576. * @return mixed this is all the return data collected from all the walk-steps
  577. *
  578. */
  579. function walk( $walkFunction , $id=0 , $returnType='string')
  580. {
  581. $useNode = $this->structure; // by default all of structure is used
  582. if ($id == 0) {
  583. $keys = array_keys($this->structure);
  584. $id = $keys[0];
  585. } else {
  586. $path = $this->getPath($id); // get the path, to be able to go to the element in this->structure
  587. array_pop($path); // pop off the last element, since it is the one requested
  588. $curNode = $this->structure; // start at the root of structure
  589. foreach ($path as $node) {
  590. $curNode = $curNode[$node['id']]; // go as deep into structure as path defines
  591. }
  592. $useNode = array(); // empty it first, so we dont have the other stuff in there from before
  593. $useNode[$id] = $curNode[$id]; // copy only the branch of the tree that the parameter $id requested
  594. }
  595. unset($this->walkReturn); // a new walk starts, unset the return value
  596. return $this->_walk( $walkFunction , $useNode , $returnType );
  597. }
  598. /**
  599. * walks through the entire tree and returns the current element and the level
  600. * so a user can use this to build a treemap or whatever
  601. *
  602. * @version 2001/06/xx
  603. * @access private
  604. * @author Wolfram Kriesing <wolfram@kriesing.de>
  605. * @param mixed $walkFunction the name of the function to call for each walk step,
  606. * or an array for a method, where
  607. * [0] is the method name and [1] the object
  608. * @param array $curLevel the reference in the this->structure, to walk everything below
  609. * @param string $returnType the return of all the walk data will be of the given type (values: string, array, ifArray)
  610. * @return mixed this is all the return data collected from all the walk-steps
  611. *
  612. */
  613. function _walk( $walkFunction , &$curLevel , $returnType )
  614. {
  615. if (sizeof($curLevel)) {
  616. foreach ($curLevel as $key=>$value) {
  617. $ret = call_user_func( $walkFunction , $this->data[$key] );
  618. switch ($returnType) {
  619. case 'array': $this->walkReturn[] = $ret;
  620. break;
  621. case 'ifArray': // this only adds the element if the $ret is an array and contains data
  622. if (is_array($ret)) {
  623. $this->walkReturn[] = $ret;
  624. }
  625. break;
  626. default: $this->walkReturn.= $ret;
  627. break;
  628. }
  629. $this->_walk( $walkFunction , $value , $returnType );
  630. }
  631. }
  632. return $this->walkReturn;
  633. } // end of function
  634. /**
  635. * adds multiple elements
  636. * you have to pass those elements in a multidimensional array which represents the
  637. * tree structure as it shall be added (this array can of course also simply contain one element)
  638. * the following array $x passed as the parameter
  639. * $x[0] = array( 'name'=>'bla','parentId'=>'30',
  640. * array( 'name'=>'bla1','comment'=>'foo',
  641. * array('name'=>'bla2'),
  642. * array('name'=>'bla2_1')
  643. * ),
  644. * array( 'name'=>'bla1_1'),
  645. * )
  646. * );
  647. * $x[1] = array( 'name'=>'fooBla','parentId'=>'30');
  648. *
  649. * would add the following tree (or subtree, or node whatever you want to call it)
  650. * under the parent with the id 30 (since 'parentId'=30 in $x[0] and in $x[1])
  651. * +--bla
  652. * | +--bla1
  653. * | | +--bla2
  654. * | | +--bla2_1
  655. * | +--bla1_1
  656. * +--fooBla
  657. *
  658. * @see add()
  659. * @version 2001/12/19
  660. * @access public
  661. * @author Wolfram Kriesing <wolfram@kriesing.de>
  662. * @param array $node the tree to be inserted, represents the tree structure,
  663. * see add() for the exact member of each node
  664. * @return mixed either boolean false on failure or the id of the inserted row
  665. */
  666. function addNode( $node )
  667. {
  668. if( sizeof($node) )
  669. foreach( $node as $aNode )
  670. {
  671. $newNode = array();
  672. foreach( $aNode as $name=>$value ) // this should always have data, if not the passed structure has an error
  673. {
  674. if( !is_array($value) ) // collect the data that need to go in the DB
  675. $newEntry[$name] = $value;
  676. else // collect the children
  677. $newNode[] = $value;
  678. }
  679. $insertedId = $this->add( $newEntry ); // add the element and get the id, that it got, to have the parentId for the children
  680. if( $insertedId!= false ) // if inserting suceeded, we have received the id under which we can insert the children
  681. {
  682. if( sizeof($newNode) ) // if there are children, set their parentId, so they kknow where they belong in the tree
  683. foreach( $newNode as $key=>$aNewNode )
  684. {
  685. $newNode[$key]['parentId'] = $insertedId;
  686. }
  687. $this->addNode( $newNode ); // call yourself recursively to insert the children, and its children and ...
  688. }
  689. }
  690. } // end of function
  691. /**
  692. * gets the path to the element given by its id
  693. * !!! ATTENTION watch out that you never change any of the data returned,
  694. * since they are references to the internal property $data
  695. *
  696. * @access public
  697. * @version 2001/10/10
  698. * @access public
  699. * @author Wolfram Kriesing <wolfram@kriesing.de>
  700. * @param mixed $id the id of the node to get the path for
  701. * @return array this array contains all elements from the root to the element given by the id
  702. *
  703. */
  704. function getPath( $id )
  705. {
  706. $path = array(); // empty the path, to be clean
  707. // FIXXME may its better to use a for(level) to count down,
  708. // since a while is always a little risky
  709. while( @$this->data[$id]['parent'] ) // until there are no more parents
  710. {
  711. $path[] = &$this->data[$id]; // curElement is already a reference, so save it in path
  712. $id = $this->data[$id]['parent']['id']; // get the next parent id, for the while to retreive the parent's parent
  713. }
  714. $path[] = &$this->data[$id]; // dont forget the last one
  715. return array_reverse($path);
  716. } // end of function
  717. /**
  718. * sets the remove-recursively mode, either true or false
  719. *
  720. * @version 2001/10/09
  721. * @access public
  722. * @author Wolfram Kriesing <wolfram@kriesing.de>
  723. * @param boolean $newValues set to true if removing a tree level shall remove all it's children and theit children ...
  724. *
  725. */
  726. function setRemoveRecursively( $case=true )
  727. {
  728. $this->removeRecursively = $case;
  729. } // end of function
  730. /**
  731. *
  732. *
  733. * @version 2002/01/21
  734. * @access private
  735. * @author Wolfram Kriesing <wolfram@kriesing.de>
  736. * @param
  737. *
  738. */
  739. function &_getElement( $id , $what='' )
  740. {
  741. if( $what=='' )
  742. {
  743. return $this->data[$id];
  744. }
  745. $elementId = $this->_getElementId( $id , $what );
  746. if( $elementId !== NULL )
  747. return $this->data[$elementId];
  748. return NULL; // we should not return false, since that might be a value of the element that is requested
  749. } // end of function
  750. /**
  751. *
  752. *
  753. * @version 2002/01/21
  754. * @access private
  755. * @author Wolfram Kriesing <wolfram@kriesing.de>
  756. * @param
  757. *
  758. */
  759. function _getElementId( $id , $what )
  760. {
  761. if( @$this->data[$id][$what] ) // use @ since the key $what might not exist
  762. return $this->data[$id][$what]['id'];
  763. return NULL;
  764. } // end of function
  765. /**
  766. * gets an element as a reference
  767. *
  768. * @version 2002/01/21
  769. * @access private
  770. * @author Wolfram Kriesing <wolfram@kriesing.de>
  771. * @param
  772. *
  773. */
  774. function &getElement( $id )
  775. {
  776. return $this->_getElement( $id );
  777. }
  778. /**
  779. *
  780. *
  781. * @version 2002/02/06
  782. * @access private
  783. * @author Wolfram Kriesing <wolfram@kriesing.de>
  784. * @param mixed either the id of an element or the path to the element
  785. *
  786. */
  787. function getElementContent( $idOrPath , $fieldName )
  788. {
  789. if( is_string($idOrPath) )
  790. {
  791. $id = $this->getIdByPath($idOrPath);
  792. }
  793. return $this->data[$id][$fieldName];
  794. }
  795. /**
  796. *
  797. *
  798. * @version 2002/02/06
  799. * @access private
  800. * @author Wolfram Kriesing <wolfram@kriesing.de>
  801. * @param
  802. *
  803. */
  804. function getElementsContent( $ids , $fieldName )
  805. {
  806. // i dont know if this method is not just overloading the file, since it only serves my lazyness
  807. // is this effective here? i can also loop in the calling code!?
  808. $fields = array();
  809. if(is_array($ids) && sizeof($ids))
  810. foreach( $ids as $aId )
  811. $fields[] = $this->getElementContent( $aId , $fieldName );
  812. return $fields;
  813. }
  814. /**
  815. * gets an element given by it's path as a reference
  816. *
  817. * @version 2002/01/21
  818. * @access public
  819. * @author Wolfram Kriesing <wolfram@kriesing.de>
  820. * @param string $path the path to search for
  821. * @param integer $startId the id where to search for the path
  822. * @param string $nodeName the name of the key that contains the node name
  823. * @param string $seperator the path seperator
  824. * @return integer the id of the searched element
  825. *
  826. */
  827. function &getElementByPath( $path , $startId=0 , $nodeName='name' , $seperator='/' )
  828. {
  829. $id = $this->getIdByPath( $path , $startId );
  830. if( $id )
  831. return $this->getElement( $id );
  832. return NULL; // return NULL since false might be interpreted as id 0
  833. }
  834. /**
  835. * gets an element ID
  836. *
  837. * @version 2002/01/21
  838. * @access public
  839. * @author Wolfram Kriesing <wolfram@kriesing.de>
  840. * @param
  841. *
  842. */
  843. /* we already have a method getIdByPath, which one should we use ????
  844. function &getElementIdByPath( $id )
  845. {
  846. return $this->_getElement( $id );
  847. }
  848. */
  849. /**
  850. * get the level, which is how far below the root are we?
  851. *
  852. * @version 2001/11/25
  853. * @access public
  854. * @author Wolfram Kriesing <wolfram@kriesing.de>
  855. * @param mixed $id the id of the node to get the level for
  856. *
  857. */
  858. function getLevel( $id )
  859. {
  860. return $this->data[$id]['level'];
  861. } // end of function
  862. /**
  863. * returns the child if the node given has one
  864. * !!! ATTENTION watch out that you never change any of the data returned,
  865. * since they are references to the internal property $data
  866. *
  867. * @version 2001/11/27
  868. * @access public
  869. * @author Wolfram Kriesing <wolfram@kriesing.de>
  870. * @param mixed $id the id of the node to get the child for
  871. *
  872. */
  873. function &getChild( $id )
  874. {
  875. return $this->_getElement( $id , 'child' );
  876. } // end of function
  877. /**
  878. * returns the child if the node given has one
  879. * !!! ATTENTION watch out that you never change any of the data returned,
  880. * since they are references to the internal property $data
  881. *
  882. * @version 2001/11/27
  883. * @access public
  884. * @author Wolfram Kriesing <wolfram@kriesing.de>
  885. * @param mixed $id the id of the node to get the child for
  886. *
  887. */
  888. function &getParent( $id )
  889. {
  890. return $this->_getElement( $id , 'parent' );
  891. } // end of function
  892. /**
  893. * returns the next element if the node given has one
  894. * !!! ATTENTION watch out that you never change any of the data returned,
  895. * since they are references to the internal property $data
  896. *
  897. * @version 2002/01/17
  898. * @access public
  899. * @author Wolfram Kriesing <wolfram@kriesing.de>
  900. * @param mixed $id the id of the node to get the child for
  901. * @return mixed reference to the next element or false if there is none
  902. */
  903. function &getNext( $id )
  904. {
  905. return $this->_getElement( $id , 'next' );
  906. } // end of function
  907. /**
  908. * returns the previous element if the node given has one
  909. * !!! ATTENTION watch out that you never change any of the data returned,
  910. * since they are references to the internal property $data
  911. *
  912. * @version 2002/02/05
  913. * @access public
  914. * @author Wolfram Kriesing <wolfram@kriesing.de>
  915. * @param mixed $id the id of the node to get the child for
  916. * @return mixed reference to the next element or false if there is none
  917. */
  918. function &getPrevious( $id )
  919. {
  920. return $this->_getElement( $id , 'previous' );
  921. } // end of function
  922. /**
  923. * returns the node for the given id
  924. * !!! ATTENTION watch out that you never change any of the data returned,
  925. * since they are references to the internal property $data
  926. *
  927. * @version 2001/11/28
  928. * @access public
  929. * @author Wolfram Kriesing <wolfram@kriesing.de>
  930. * @param mixed $id the id of the node to get
  931. *
  932. */
  933. /* this should be getElement, i think it was a bit weird that i made this method like this
  934. function &getNode( $id )
  935. {
  936. return $this->_getElement( $id );
  937. } // end of function
  938. */
  939. /**
  940. * return the id of the element which is referenced by $path
  941. * this is useful for xml-structures, like: getIdByPath( '/root/sub1/sub2' )
  942. * this requires the structure to use each name uniquely
  943. * if this is not given it will return the first proper path found
  944. * i.e. there should only be one path /x/y/z
  945. *
  946. * @version 2001/11/28
  947. * @access public
  948. * @author Wolfram Kriesing <wolfram@kriesing.de>
  949. * @param string $path the path to search for
  950. * @param integer $startId the id where to search for the path
  951. * @param string $nodeName the name of the key that contains the node name
  952. * @param string $seperator the path seperator
  953. * @return integer the id of the searched element
  954. *
  955. */
  956. function getIdByPath( $path , $startId=0 , $nodeName='name' , $seperator='/' )
  957. // should this method be called getElementIdByPath ????
  958. {
  959. // if no start ID is given get the root
  960. if( $startId==0 )
  961. $startId = $this->getFirstRootId();
  962. else // if a start id is given, get its first child to start searching there
  963. {
  964. $startId = $this->getChildId($startId);
  965. if( $startId == false ) // is there a child to this element?
  966. return false;
  967. }
  968. if( strpos( $path , $seperator )===0 ) // if a seperator is at the beginning strip it off
  969. $path = substr( $path , strlen($seperator) );
  970. $nodes = explode( $seperator , $path );
  971. $curId = $startId;
  972. foreach( $nodes as $key=>$aNodeName )
  973. {
  974. $nodeFound = false;
  975. do
  976. {
  977. //print "search $aNodeName, in ".$this->data[$curId][$nodeName]."<br>";
  978. if( $this->data[$curId][$nodeName] == $aNodeName )
  979. {
  980. $nodeFound = true;
  981. // do only save the child if we are not already at the end of path
  982. // because then we need curId to return it
  983. if( $key < (sizeof($nodes)-1) )
  984. $curId = $this->getChildId($curId);
  985. break;
  986. }
  987. $curId = $this->getNextId($curId);
  988. //print "curId = $curId<br>";
  989. }
  990. while( $curId );
  991. if( $nodeFound==false )
  992. {
  993. //print 'NOT FOUND<br><br>';
  994. return false;
  995. }
  996. }
  997. //print '<br>';
  998. return $curId;
  999. // FIXXME to be implemented
  1000. } // end of function
  1001. /**
  1002. * this gets the first element that is in the root node
  1003. * i think that there can't be a "getRoot" method since there might
  1004. * be multiple number of elements in the root node, at least the
  1005. * way it works now
  1006. *
  1007. * @access public
  1008. * @version 2001/12/10
  1009. * @author Wolfram Kriesing <wolfram@kriesing.de>
  1010. * @return returns the first root element
  1011. */
  1012. function &getFirstRoot()
  1013. {
  1014. // could also be reset($this->data) i think, since php keeps the order ... but i didnt try
  1015. reset($this->structure);
  1016. return $this->data[key($this->structure)];
  1017. } // end of function
  1018. /**
  1019. * since in a nested tree there can only be one root
  1020. * which i think (now) is correct, we also need an alias for this method
  1021. * this also makes all the methods in Tree_Common, which access the
  1022. * root element work properly!
  1023. *
  1024. * @access public
  1025. * @version 2002/07/26
  1026. * @author Wolfram Kriesing <wolfram@kriesing.de>
  1027. * @return returns the first root element
  1028. */
  1029. function &getRoot()
  1030. {
  1031. return $this->getFirstRoot();
  1032. }
  1033. /**
  1034. * gets the tree under the given element in one array, sorted
  1035. * so you can go through the elements from begin to end and list them
  1036. * as they are in the tree, where every child (until the deepest) is retreived
  1037. *
  1038. * @see &_getNode()
  1039. * @access public
  1040. * @version 2001/12/17
  1041. * @author Wolfram Kriesing <wolfram@kriesing.de>
  1042. * @param integer $startId the id where to start walking
  1043. * @param integer $depth this number says how deep into
  1044. * the structure the elements shall be retreived
  1045. * @return array sorted as listed in the tree
  1046. */
  1047. function &getNode( $startId=0 , $depth=0 )
  1048. {
  1049. if ($startId == 0) {
  1050. $level = 0;
  1051. } else {
  1052. $level = $this->getLevel($startId);
  1053. }
  1054. $this->_getNodeMaxLevel = $depth ? ($depth + $level) : 0 ;
  1055. //!!! $this->_getNodeCurParent = $this->data['parent']['id'];
  1056. // if the tree is empty dont walk through it
  1057. if (!sizeof($this->data)) {
  1058. return;
  1059. }
  1060. $ret = $this->walk( array(&$this,'_getNode') , $startId , 'ifArray' );
  1061. return $ret;
  1062. } // end of function
  1063. /**
  1064. * this is used for walking through the tree structure
  1065. * until a given level, this method should only be used by getNode
  1066. *
  1067. * @see &getNode()
  1068. * @see walk()
  1069. * @see _walk()
  1070. * @access private
  1071. * @version 2001/12/17
  1072. * @author Wolfram Kriesing <wolfram@kriesing.de>
  1073. * @param array $node the node passed by _walk
  1074. * @return mixed either returns the node, or nothing if the level _getNodeMaxLevel is reached
  1075. */
  1076. function &_getNode( &$node )
  1077. {
  1078. if ($this->_getNodeMaxLevel) {
  1079. if ($this->getLevel($node['id']) < $this->_getNodeMaxLevel) {
  1080. return $node;
  1081. }
  1082. return;
  1083. }
  1084. return $node;
  1085. } // end of function
  1086. /**
  1087. * returns if the given element has any children
  1088. *
  1089. * @version 2001/12/17
  1090. * @access public
  1091. * @author Wolfram Kriesing <wolfram@kriesing.de>
  1092. * @param integer $id the id of the node to check for children
  1093. * @return boolean true if the node has children
  1094. */
  1095. function hasChildren( $id=0 )
  1096. {
  1097. if( isset($this->data[$id]['children']) && sizeof($this->data[$id]['children']) > 0 )
  1098. return true;
  1099. return false;
  1100. } // end of function
  1101. /**
  1102. * returns the children of the given ids
  1103. *
  1104. * @version 2001/12/17
  1105. * @access public
  1106. * @author Wolfram Kriesing <wolfram@kriesing.de>
  1107. * @param integer $id the id of the node to check for children
  1108. * @param integer the children of how many levels shall be returned
  1109. * @return boolean true if the node has children
  1110. */
  1111. function getChildren( $ids , $levels=1 )
  1112. {
  1113. //FIXXME $levels to be implemented
  1114. $ret = array();
  1115. if (is_array($ids)) {
  1116. foreach ($ids as $aId) {
  1117. if ($this->hasChildren( $aId )) {
  1118. $ret[$aId] = $this->data[$aId]['children'];
  1119. }
  1120. }
  1121. } else {
  1122. if ($this->hasChildren( $ids )) {
  1123. $ret = $this->data[$ids]['children'];
  1124. }
  1125. }
  1126. return $ret;
  1127. } // end of function
  1128. /**
  1129. * returns if the given element is a valid node
  1130. *
  1131. * @version 2001/12/21
  1132. * @access public
  1133. * @author Wolfram Kriesing <wolfram@kriesing.de>
  1134. * @param integer $id the id of the node to check for children
  1135. * @return boolean true if the node has children
  1136. */
  1137. function isNode( $id=0 )
  1138. {
  1139. return isset($this->data[$id]);
  1140. } // end of function
  1141. /**
  1142. * this is for debugging, dumps the entire data-array
  1143. * an extra method is needed, since this array contains recursive
  1144. * elements which make a normal print_f or var_dump not show all the data
  1145. *
  1146. * @version 2002/01/21
  1147. * @access public
  1148. * @author Wolfram Kriesing <wolfram@kriesing.de>
  1149. * @params mixed $node either the id of the node to dump, this will dump everything below the given node
  1150. * or an array of nodes, to dump, this only dumps the elements passed as an array
  1151. * or 0 or no parameter if the entire tree shall be dumped
  1152. * if you want to dump only a single element pass it as an array using
  1153. * array($element)
  1154. */
  1155. function varDump( $node=0 )
  1156. {
  1157. $dontDump = array('parent','child','children','next','previous');
  1158. // if $node is an array, we assume it is a collection of elements
  1159. if( !is_array($node) )
  1160. $nodes = $this->getNode($node); // if $node==0 then the entire tree is retreived
  1161. if (sizeof($node)) {
  1162. print '<table border="1"><tr><th>name</th>';
  1163. $keys = array();
  1164. foreach ($this->getRoot() as $key=>$x) {
  1165. if (!is_array($x)) {
  1166. print "<th>$key</th>";
  1167. $keys[] = $key;
  1168. }
  1169. }
  1170. print "</tr>";
  1171. foreach ($nodes as $aNode) {
  1172. print '<tr><td nowrap="nowrap">';
  1173. $prefix = '';
  1174. for($i=0;$i<$aNode['level'];$i++) $prefix .= '- ';
  1175. print "$prefix {$aNode['name']}</td>";
  1176. foreach ($keys as $aKey) {
  1177. if (!is_array($key)) {
  1178. $val = $aNode[$aKey] ? $aNode[$aKey] : '&nbsp;';
  1179. print "<td>$val</td>";
  1180. }
  1181. }
  1182. print "</tr>";
  1183. }
  1184. print "</table>";
  1185. }
  1186. } // end of function
  1187. //### TODO's ###
  1188. /**
  1189. * NOT IMPLEMENTED YET
  1190. * copies a part of the tree under a given parent
  1191. *
  1192. * @version 2001/12/19
  1193. * @access public
  1194. * @author Wolfram Kriesing <wolfram@kriesing.de>
  1195. * @param $srcId the id of the element which is copied, all its children are copied too
  1196. * @param $destId the id that shall be the new parent
  1197. * @return boolean true on success
  1198. *
  1199. */
  1200. function copy( $srcId , $destId )
  1201. {
  1202. if( method_exists($this->dataSourceClass,'copy') )
  1203. return $this->dataSourceClass->copy( $srcId , $destId );
  1204. else
  1205. return $this->_throwError( 'method not implemented yet.' , __LINE__ );
  1206. /*
  1207. remove all array elements after 'parent' since those had been created
  1208. and remove id and set parentId and that should be it, build the tree and pass it to addNode
  1209. those are the fields in one data-entry
  1210. id=>41
  1211. parentId=>39
  1212. name=>Java
  1213. parent=>Array
  1214. prevId=>58
  1215. previous=>Array
  1216. childId=>77
  1217. child=>Array
  1218. nextId=>104
  1219. next=>Array
  1220. children=>Array
  1221. level=>2
  1222. $this->getNode
  1223. foreach( $this->data[$srcId] as $key=>$value )
  1224. print("$key=>$value<br>");
  1225. */
  1226. } // end of function
  1227. } // end of class
  1228. ?>