5 * @author Roland Haeder <webmaster@shipsimu.org>
7 * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Core Developer Team
8 * @license GNU GPL 3.0 or any newer version
9 * @link http://www.shipsimu.org
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.
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.
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/>.
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;
33 private $listGroups = array();
38 private $listEntries = array();
43 private $listIndex = array();
46 * Protected constructor
48 * @param $className Name of the class
51 protected function __construct ($className) {
52 // Call parent constructor
53 parent::__construct($className);
57 * Getter for iterator instance from this list
59 * @return $iteratorInstance An instance of a Iterator class
61 public function getIterator () {
62 // Get iterator from here
63 $iteratorInstance = $this->getIteratorInstance();
65 // Is the instance set?
66 if (is_null($iteratorInstance)) {
67 // Prepare a default iterator
68 $iteratorInstance = ObjectFactory::createObjectByConfiguredName('default_iterator_class', array($this));
71 $this->setIteratorInstance($iteratorInstance);
75 return $iteratorInstance;
79 * Checks whether the given group is set
81 * @param $groupName Group to check if found in list
82 * @return $isset Whether the group is valid
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]);
90 * Adds the given group or if already added issues a ListGroupAlreadyAddedException
92 * @param $groupName Group to add
94 * @throws ListGroupAlreadyAddedException If the given group is already added
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);
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!');
110 * Adds the given instance to list group and sub group
112 * @param $groupName Group to add instance to
113 * @param $subGroup Sub group to add instance to
114 * @param $visitableInstance An instance of Visitable
116 * @throws NoListGroupException If the given group is not found
118 public function addInstance ($groupName, $subGroup, Visitable $visitableInstance) {
120 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',subGroup=' . $subGroup . ',visitableInstance=' . $visitableInstance->__toString() . ' - CALLED!');
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);
128 // Is the sub group there?
129 if (!$this->listGroups[$groupName]->isGroupSet($subGroup)) {
130 // Automatically add it
131 $this->listGroups[$groupName]->addGroup($subGroup);
135 $hash = $this->generateHash($groupName, $subGroup, $visitableInstance);
138 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',subGroup=' . $subGroup . ',hash=' . $hash . ' - Calling addEntry() ...');
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);
144 // Add the hash to the index
145 array_push($this->listIndex, $hash);
147 // Add the instance itself to the list
148 $this->listEntries[$hash] = $visitableInstance;
151 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',subGroup=' . $subGroup . ' - EXIT!');
155 * Gets an array from given list
157 * @param $list The requested list
158 * @return $array The requested array
159 * @throws NoListGroupException If the given group is not found
161 public final function getArrayFromList ($list) {
163 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',list[' . gettype($list) . ']=' . $list . ' - CALLED!');
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);
174 // Is there another list?
175 if (!is_null($list)) {
176 // Then get it as well
177 $array = $this->listGroups[$list]->getArrayFromList(NULL);
180 // Walk through all entries
181 foreach ($this->listIndex as $hash) {
183 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: hash=' . $hash);
185 // Is the list entry set?
186 if ($this->isHashValid($hash)) {
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!');
194 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',list[' . gettype($list) . ']=' . $list . ',array()=' . count($array) . ' - EXIT!');
201 * Adds the given entry to list group
203 * @param $groupName Group to add instance to
204 * @param $entry An entry of any type
206 * @throws NoListGroupException If the given group is not found
208 public function addEntry ($groupName, $entry) {
210 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ' - CALLED!');
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);
219 $hash = $this->generateHash($groupName, $groupName, $entry);
222 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',entry=' . print_r($entry, TRUE) . ', hash=' . $hash);
224 // Add the hash to the index
225 array_push($this->listIndex, $hash);
228 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',listEntries()=' . count($this->listEntries));
230 // Now add the entry to the list
231 $this->listEntries[$hash] = $entry;
234 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',listEntries()=' . count($this->listEntries) . ' - EXIT!');
238 * Removes given entry from the list group
240 * @param $groupName Group where we should remove the entry from
241 * @param $entry The entry we should remove
243 * @throws NoListGroupException If the given group is not found
245 public function removeEntry ($groupName, $entry) {
247 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ' - CALLED!');
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);
256 $hash = $this->generateHash($groupName, $groupName, $entry);
259 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',entry=' . $entry . ', hash=' . $hash);
261 // Remove it from the list ...
262 unset($this->listEntries[$hash]);
264 // ... and hash list as well
265 unset($this->listIndex[array_search($hash, $this->listIndex)]);
268 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ' - EXIT!');
272 * Generates a hash from given group, sub group and entry
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
279 private function generateHash ($groupName, $subGroup, $entry) {
280 // Created entry, 'null' is default
283 // Determine type of entry
284 if (is_null($entry)) {
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)) {
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]);
302 // Unsupported type detected
303 self::createDebugInstance(__CLASS__)->debugOutput('BASE-LIST[' . __METHOD__ . ':' . __LINE__ . ']: Entry type ' . gettype($entry) . ' is unsupported.');
305 // @TODO Extend this somehow?
306 $entry2 = gettype($entry);
309 // Construct string which we shall hash
310 $hashString = $groupName . ':' . $subGroup . ':' . $entry2;
312 // Hash it with fastest hasher
313 $hash = crc32($hashString);
320 * Clears an array of groups, all are being checked for existence
322 * @param $groupNames An array with existing list groups
325 protected function clearGroups (array $groupNames) {
326 // Walk through all groups
327 foreach ($groupNames as $groupName) {
329 $this->clearGroup($groupName);
334 * Clears a single group by resetting it to its initial state (empty array)
336 * @param $groupName Name of an existing group to clear
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);
346 // Then clear this group list
347 $this->listGroups[$groupName]->clearList();
350 $this->listIndex = array();
351 $this->listEntries = array();
355 * Counts all entries in this list
357 * @return $count All entries in this list
359 public final function count () {
360 return count($this->listIndex);
364 * Checks whether the given hash is valid
366 * @param $hash The hash we should validate
367 * @return $isValid Whether the given hash is valid
369 public final function isHashValid ($hash) {
371 $isValid = ((in_array($hash, $this->listIndex)) && (isset($this->listEntries[$hash])));
378 * Getter for hash from given hash index
380 * @param $hashIndex Index holding the hash
381 * @return $hash The hash
383 public final function getHash ($hashIndex) {
385 $hash = $this->listIndex[$hashIndex];
392 * Gets an entry from given hash index
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
398 public function getEntry ($hashIndex) {
399 // Get the hash value
400 $hash = $this->getHash($hashIndex);
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);
408 // Now copy the entry
409 $entry = $this->listEntries[$hash];
416 * Gets a full array from given group name
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
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);
429 // Init the entries' array
433 $iteratorInstance = $this->listGroups[$groupName]->getIterator();
435 // Rewind the iterator for this round
436 $iteratorInstance->rewind();
438 // Go through all entries
439 while ($iteratorInstance->valid()) {
441 $entryIndex = $iteratorInstance->key();
443 // ... and the final entry which is the stored instance
444 $entry = $this->getEntry($entryIndex);
447 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('LIST: Adding entry ' . $entry . ' ...');
449 // Add it to the list
450 $entries[$iteratorInstance->current()] = $entry;
453 $iteratorInstance->next();
461 * Updates the given entry by hash with given array
463 * @param $hash Hash for this entry
464 * @param $entryArray Array with entry we should update
466 * @throws InvalidListHashException If the solved hash index is invalid
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);
476 $this->listEntries[$hash] = $entryArray;