Debugging continued:
[core.git] / inc / main / classes / lists / class_BaseList.php
1 <?php
2 // Own namespace
3 namespace CoreFramework\Lists;
4
5 // Import framework stuff
6 use CoreFramework\Factory\ObjectFactory;
7 use CoreFramework\Generic\FrameworkInterface;
8 use CoreFramework\Object\BaseFrameworkSystem;
9
10 /**
11  * A general list class
12  *
13  * @author              Roland Haeder <webmaster@shipsimu.org>
14  * @version             0.0.0
15  * @copyright   Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Core Developer Team
16  * @license             GNU GPL 3.0 or any newer version
17  * @link                http://www.shipsimu.org
18  *
19  * This program is free software: you can redistribute it and/or modify
20  * it under the terms of the GNU General Public License as published by
21  * the Free Software Foundation, either version 3 of the License, or
22  * (at your option) any later version.
23  *
24  * This program is distributed in the hope that it will be useful,
25  * but WITHOUT ANY WARRANTY; without even the implied warranty of
26  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
27  * GNU General Public License for more details.
28  *
29  * You should have received a copy of the GNU General Public License
30  * along with this program.  If not, see <http://www.gnu.org/licenses/>.
31  */
32 class BaseList extends BaseFrameworkSystem implements IteratorAggregate, Countable {
33         // Exception constants
34         const EXCEPTION_GROUP_ALREADY_ADDED = 0xf20;
35         const EXCEPTION_GROUP_NOT_FOUND     = 0xf21;
36         const EXCEPTION_INVALID_HASH        = 0xf22;
37
38         /**
39          * List groups array
40          */
41         private $listGroups = array();
42
43         /**
44          * List entries array
45          */
46         private $listEntries = array();
47
48         /**
49          * List index array
50          */
51         private $listIndex = array();
52
53         /**
54          * Protected constructor
55          *
56          * @param       $className      Name of the class
57          * @return      void
58          */
59         protected function __construct ($className) {
60                 // Call parent constructor
61                 parent::__construct($className);
62         }
63
64         /**
65          * Getter for iterator instance from this list
66          *
67          * @return      $iteratorInstance       An instance of a Iterator class
68          */
69         public function getIterator () {
70                 // Get iterator from here
71                 $iteratorInstance = $this->getIteratorInstance();
72
73                 // Is the instance set?
74                 if (is_null($iteratorInstance)) {
75                         // Prepare a default iterator
76                         $iteratorInstance = ObjectFactory::createObjectByConfiguredName('default_iterator_class', array($this));
77
78                         // Set it here
79                         $this->setIteratorInstance($iteratorInstance);
80                 } // END - if
81
82                 // And return it
83                 return $iteratorInstance;
84         }
85
86         /**
87          * Checks whether the given group is set
88          *
89          * @param       $groupName      Group to check if found in list
90          * @return      $isset          Whether the group is valid
91          */
92         public function isGroupSet ($groupName) {
93                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName);
94                 return isset($this->listGroups[$groupName]);
95         }
96
97         /**
98          * Adds the given group or if already added issues a ListGroupAlreadyAddedException
99          *
100          * @param       $groupName      Group to add
101          * @return      void
102          * @throws      ListGroupAlreadyAddedException  If the given group is already added
103          */
104         public function addGroup ($groupName) {
105                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ' - CALLED!');
106                 // Is the group already added?
107                 if ($this->isGroupSet($groupName)) {
108                         // Throw the exception here
109                         throw new ListGroupAlreadyAddedException(array($this, $groupName), self::EXCEPTION_GROUP_ALREADY_ADDED);
110                 } // END - if
111
112                 // Add the group which is a simple array
113                 $this->listGroups[$groupName] = ObjectFactory::createObjectByConfiguredName('list_group_class');
114                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ' - EXIT!');
115         }
116
117         /**
118          * Adds the given instance to list group and sub group
119          *
120          * @param       $groupName                      Group to add instance to
121          * @param       $subGroup                       Sub group to add instance to
122          * @param       $visitableInstance      An instance of Visitable
123          * @return      void
124          * @throws      NoListGroupException    If the given group is not found
125          */
126         public function addInstance ($groupName, $subGroup, Visitable $visitableInstance) {
127                 // Debug message
128                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName  . ',subGroup=' . $subGroup . ',visitableInstance=' . $visitableInstance->__toString() . ' - CALLED!');
129
130                 // Is the group there?
131                 if (!$this->isGroupSet($groupName)) {
132                         // Throw the exception here
133                         throw new NoListGroupException(array($this, $groupName), self::EXCEPTION_GROUP_NOT_FOUND);
134                 } // END - if
135
136                 // Is the sub group there?
137                 if (!$this->listGroups[$groupName]->isGroupSet($subGroup)) {
138                         // Automatically add it
139                         $this->listGroups[$groupName]->addGroup($subGroup);
140                 } // END - if
141
142                 // Generate the hash
143                 $hash = $this->generateHash($groupName, $subGroup, $visitableInstance);
144
145                 // Debug message
146                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName  . ',subGroup=' . $subGroup . ',hash=' . $hash . ' - Calling addEntry() ...');
147
148                 // Now add it to the group list and hash it
149                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',this->listGroups[' . $groupName . ']=' . $this->listGroups[$groupName]->__toString());
150                 //$this->listGroups[$groupName]->addEntry($subGroup, $hash);
151
152                 // Add the hash to the index
153                 array_push($this->listIndex, $hash);
154
155                 // Add the instance itself to the list
156                 $this->listEntries[$hash] = $visitableInstance;
157
158                 // Debug message
159                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName  . ',subGroup=' . $subGroup . ' - EXIT!');
160         }
161
162         /**
163          * Gets an array from given list
164          *
165          * @param       $list   The requested list
166          * @return      $array  The requested array
167          * @throws      NoListGroupException    If the given group is not found
168          */
169         public final function getArrayFromList ($list) {
170                 // Debug message
171                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',list[' . gettype($list) . ']=' . $list . ' - CALLED!');
172
173                 // Is the group there?
174                 if ((!is_null($list)) && (!$this->isGroupSet($list))) {
175                         // Throw the exception here
176                         throw new NoListGroupException(array($this, $list), self::EXCEPTION_GROUP_NOT_FOUND);
177                 } // END - if
178
179                 // Init array
180                 $array = array();
181
182                 // Is there another list?
183                 if (!is_null($list)) {
184                         // Then get it as well
185                         $array = $this->listGroups[$list]->getArrayFromList(NULL);
186                 } // END - if
187
188                 // Walk through all entries
189                 foreach ($this->listIndex as $hash) {
190                         // Debug message
191                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: hash=' . $hash);
192
193                         // Is the list entry set?
194                         if ($this->isHashValid($hash)) {
195                                 // Add it
196                                 array_push($array, $this->listEntries[$hash]);
197                                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: hash=' . $hash . ',array(' . count($array) . ')=' . print_r($array, TRUE) . ' - ADDED!');
198                         } // END - if
199                 } // END - foreach
200
201                 // Debug message
202                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',list[' . gettype($list) . ']=' . $list . ',array()=' . count($array) . ' - EXIT!');
203
204                 // Return it
205                 return $array;
206         }
207
208         /**
209          * Adds the given entry to list group
210          *
211          * @param       $groupName      Group to add instance to
212          * @param       $entry          An entry of any type
213          * @return      void
214          * @throws      NoListGroupException    If the given group is not found
215          */
216         public function addEntry ($groupName, $entry) {
217                 // Debug message
218                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ' - CALLED!');
219
220                 // Is the group already added?
221                 if (!$this->isGroupSet($groupName)) {
222                         // Throw the exception here
223                         throw new NoListGroupException(array($this, $groupName), self::EXCEPTION_GROUP_NOT_FOUND);
224                 } // END - if
225
226                 // Generate hash
227                 $hash = $this->generateHash($groupName, $groupName, $entry);
228
229                 // Debug message
230                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',entry=' . print_r($entry, TRUE) . ', hash=' . $hash);
231
232                 // Add the hash to the index
233                 array_push($this->listIndex, $hash);
234
235                 // Debug message
236                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',listEntries()=' . count($this->listEntries));
237
238                 // Now add the entry to the list
239                 $this->listEntries[$hash] = $entry;
240
241                 // Debug message
242                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',listEntries()=' . count($this->listEntries) . ' - EXIT!');
243         }
244
245         /**
246          * Removes given entry from the list group
247          *
248          * @param       $groupName      Group where we should remove the entry from
249          * @param       $entry          The entry we should remove
250          * @return      void
251          * @throws      NoListGroupException    If the given group is not found
252          */
253         public function removeEntry ($groupName, $entry) {
254                 // Debug message
255                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ' - CALLED!');
256
257                 // Is the group already added?
258                 if (!$this->isGroupSet($groupName)) {
259                         // Throw the exception here
260                         throw new NoListGroupException(array($this, $groupName), self::EXCEPTION_GROUP_NOT_FOUND);
261                 } // END - if
262
263                 // Generate hash
264                 $hash = $this->generateHash($groupName, $groupName, $entry);
265
266                 // Debug message
267                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',entry=' . $entry . ', hash=' . $hash);
268
269                 // Remove it from the list ...
270                 unset($this->listEntries[$hash]);
271
272                 // ... and hash list as well
273                 unset($this->listIndex[array_search($hash, $this->listIndex)]);
274
275                 // Debug message
276                 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ' - EXIT!');
277         }
278
279         /**
280          * Generates a hash from given group, sub group and entry
281          *
282          * @param       $groupName      Group to add instance to
283          * @param       $subGroup       Sub group to add instance to
284          * @param       $entry          An entry of any type
285          * @return      $hash           The generated
286          */
287         private function generateHash ($groupName, $subGroup, $entry) {
288                 // Created entry, 'null' is default
289                 $entry2 = 'null';
290
291                 // Determine type of entry
292                 if (is_null($entry)) {
293                         // Ignore this
294                 } elseif ($entry instanceof FrameworkInterface) {
295                         // Own instance detected
296                         $entry2 = $entry->hashCode();
297                 } elseif ((is_int($entry)) || (is_float($entry)) || (is_resource($entry))) {
298                         // Integer/float/resource detected
299                         $entry2 = gettype($entry) . ':' . $entry;
300                 } elseif (is_string($entry)) {
301                         // String found
302                         $entry2 = crc32($entry) . ':' . strlen($entry);
303                 } elseif ((is_array($entry)) && (isset($entry['id']))) {
304                         // Supported array found
305                         $entry2 = crc32($entry['id']) . ':' . count($entry);
306                 } elseif ((is_array($entry)) && (isset($entry[BasePool::SOCKET_ARRAY_RESOURCE])) && (isset($entry[BasePool::SOCKET_ARRAY_CONN_TYPE]))) {
307                         // Is a socket resource array
308                         $entry2 = crc32($entry[BasePool::SOCKET_ARRAY_RESOURCE] . ':' . $entry[BasePool::SOCKET_ARRAY_CONN_TYPE]);
309                 } else {
310                         // Unsupported type detected
311                         self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-LIST[' . __METHOD__ . ':' . __LINE__ . ']: Entry type ' . gettype($entry) . ' is unsupported.');
312
313                         // @TODO Extend this somehow?
314                         $entry2 = gettype($entry);
315                 }
316
317                 // Construct string which we shall hash
318                 $hashString = $groupName . ':' . $subGroup . ':' . $entry2;
319
320                 // Hash it with fastest hasher
321                 $hash = crc32($hashString);
322
323                 // And return it
324                 return $hash;
325         }
326
327         /**
328          * Clears an array of groups, all are being checked for existence
329          *
330          * @param       $groupNames             An array with existing list groups
331          * @return      void
332          */
333         protected function clearGroups (array $groupNames) {
334                 // Walk through all groups
335                 foreach ($groupNames as $groupName) {
336                         // Clear this group
337                         $this->clearGroup($groupName);
338                 } // END - foreach
339         }
340
341         /**
342          * Clears a single group by resetting it to its initial state (empty array)
343          *
344          * @param       $groupName      Name of an existing group to clear
345          * @return      void
346          */
347         protected function clearGroup ($groupName) {
348                 // Does this group exist?
349                 if (!$this->isGroupSet($groupName)) {
350                         // Throw the exception here
351                         throw new NoListGroupException(array($this, $groupName), self::EXCEPTION_GROUP_NOT_FOUND);
352                 } // END - if
353
354                 // Then clear this group list
355                 $this->listGroups[$groupName]->clearList();
356
357                 // Clear this list
358                 $this->listIndex = array();
359                 $this->listEntries = array();
360         }
361         
362         /**
363          * Counts all entries in this list
364          *
365          * @return      $count  All entries in this list
366          */
367         public final function count () {
368                 return count($this->listIndex);
369         }
370
371         /**
372          * Checks whether the given hash is valid
373          *
374          * @param       $hash           The hash we should validate
375          * @return      $isValid        Whether the given hash is valid
376          */
377         public final function isHashValid ($hash) {
378                 // Check it
379                 $isValid = ((in_array($hash, $this->listIndex)) && (isset($this->listEntries[$hash])));
380
381                 // Return the result
382                 return $isValid;
383         }
384
385         /**
386          * Getter for hash from given hash index
387          *
388          * @param       $hashIndex      Index holding the hash
389          * @return      $hash           The hash
390          */
391         public final function getHash ($hashIndex) {
392                 // Get it ...
393                 $hash = $this->listIndex[$hashIndex];
394
395                 // ... and return it
396                 return $hash;
397         }
398
399         /**
400          * Gets an entry from given hash index
401          *
402          * @param       $hashIndex      The hash index to resolve the mapped entry
403          * @return      $entry          Solved entry from list
404          * @throws      InvalidListHashException        If the solved hash index is invalid
405          */
406         public function getEntry ($hashIndex) {
407                 // Get the hash value
408                 $hash = $this->getHash($hashIndex);
409
410                 // Is the hash valid?
411                 if (!$this->isHashValid($hash)) {
412                         // Throw an exception here
413                         throw new InvalidListHashException(array($this, $hash, $hashIndex), self::EXCEPTION_INVALID_HASH);
414                 } // END - if
415
416                 // Now copy the entry
417                 $entry = $this->listEntries[$hash];
418
419                 // Return it
420                 return $entry;
421         }
422
423         /**
424          * Gets a full array from given group name
425          *
426          * @param       $groupName      The group name to get a list for
427          * @return      $entries        The array with all entries
428          * @throws      NoListGroupException    If the specified group is invalid
429          */
430         public function getArrayFromProtocolInstance ($groupName) {
431                 // Is the group valid?
432                 if (!$this->isGroupSet($groupName)) {
433                         // Throw the exception here
434                         throw new NoListGroupException(array($this, $groupName), self::EXCEPTION_GROUP_NOT_FOUND);
435                 } // END - if
436
437                 // Init the entries' array
438                 $entries = array();
439
440                 // Get an iterator
441                 $iteratorInstance = $this->listGroups[$groupName]->getIterator();
442
443                 // Rewind the iterator for this round
444                 $iteratorInstance->rewind();
445
446                 // Go through all entries
447                 while ($iteratorInstance->valid()) {
448                         // Get key
449                         $entryIndex = $iteratorInstance->key();
450
451                         // ... and the final entry which is the stored instance
452                         $entry = $this->getEntry($entryIndex);
453
454                         // Debug message
455                         //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('LIST: Adding entry ' . $entry . ' ...');
456
457                         // Add it to the list
458                         $entries[$iteratorInstance->current()] = $entry;
459
460                         // Skip to next one
461                         $iteratorInstance->next();
462                 } // END - while
463
464                 // Return the list
465                 return $entries;
466         }
467
468         /**
469          * Updates the given entry by hash with given array
470          *
471          * @param       $hash           Hash for this entry
472          * @param       $entryArray     Array with entry we should update
473          * @return      void
474          * @throws      InvalidListHashException        If the solved hash index is invalid
475          */
476         public function updateCurrentEntryByHash ($hash, array $entryArray) {
477                 // Is the hash valid?
478                 if (!$this->isHashValid($hash)) {
479                         // Throw an exception here, hashIndex is unknown at this point
480                         throw new InvalidListHashException(array($this, $hash, -999), self::EXCEPTION_INVALID_HASH);
481                 } // END - if
482
483                 // Set the entry
484                 $this->listEntries[$hash] = $entryArray;
485         }
486
487 }