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