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