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