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