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