3 namespace CoreFramework\Lists;
5 // Load framework stuff
6 use CoreFramework\Generic\FrameworkInterface;
11 * @author Roland Haeder <webmaster@shipsimu.org>
13 * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Core Developer Team
14 * @license GNU GPL 3.0 or any newer version
15 * @link http://www.shipsimu.org
17 * This program is free software: you can redistribute it and/or modify
18 * it under the terms of the GNU General Public License as published by
19 * the Free Software Foundation, either version 3 of the License, or
20 * (at your option) any later version.
22 * This program is distributed in the hope that it will be useful,
23 * but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU General Public License for more details.
27 * You should have received a copy of the GNU General Public License
28 * along with this program. If not, see <http://www.gnu.org/licenses/>.
30 class BaseList extends BaseFrameworkSystem implements IteratorAggregate, Countable {
31 // Exception constants
32 const EXCEPTION_GROUP_ALREADY_ADDED = 0xf20;
33 const EXCEPTION_GROUP_NOT_FOUND = 0xf21;
34 const EXCEPTION_INVALID_HASH = 0xf22;
39 private $listGroups = array();
44 private $listEntries = array();
49 private $listIndex = array();
52 * Protected constructor
54 * @param $className Name of the class
57 protected function __construct ($className) {
58 // Call parent constructor
59 parent::__construct($className);
63 * Getter for iterator instance from this list
65 * @return $iteratorInstance An instance of a Iterator class
67 public function getIterator () {
68 // Get iterator from here
69 $iteratorInstance = $this->getIteratorInstance();
71 // Is the instance set?
72 if (is_null($iteratorInstance)) {
73 // Prepare a default iterator
74 $iteratorInstance = ObjectFactory::createObjectByConfiguredName('default_iterator_class', array($this));
77 $this->setIteratorInstance($iteratorInstance);
81 return $iteratorInstance;
85 * Checks whether the given group is set
87 * @param $groupName Group to check if found in list
88 * @return $isset Whether the group is valid
90 public function isGroupSet ($groupName) {
91 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName);
92 return isset($this->listGroups[$groupName]);
96 * Adds the given group or if already added issues a ListGroupAlreadyAddedException
98 * @param $groupName Group to add
100 * @throws ListGroupAlreadyAddedException If the given group is already added
102 public function addGroup ($groupName) {
103 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ' - CALLED!');
104 // Is the group already added?
105 if ($this->isGroupSet($groupName)) {
106 // Throw the exception here
107 throw new ListGroupAlreadyAddedException(array($this, $groupName), self::EXCEPTION_GROUP_ALREADY_ADDED);
110 // Add the group which is a simple array
111 $this->listGroups[$groupName] = ObjectFactory::createObjectByConfiguredName('list_group_class');
112 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ' - EXIT!');
116 * Adds the given instance to list group and sub group
118 * @param $groupName Group to add instance to
119 * @param $subGroup Sub group to add instance to
120 * @param $visitableInstance An instance of Visitable
122 * @throws NoListGroupException If the given group is not found
124 public function addInstance ($groupName, $subGroup, Visitable $visitableInstance) {
126 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',subGroup=' . $subGroup . ',visitableInstance=' . $visitableInstance->__toString() . ' - CALLED!');
128 // Is the group there?
129 if (!$this->isGroupSet($groupName)) {
130 // Throw the exception here
131 throw new NoListGroupException(array($this, $groupName), self::EXCEPTION_GROUP_NOT_FOUND);
134 // Is the sub group there?
135 if (!$this->listGroups[$groupName]->isGroupSet($subGroup)) {
136 // Automatically add it
137 $this->listGroups[$groupName]->addGroup($subGroup);
141 $hash = $this->generateHash($groupName, $subGroup, $visitableInstance);
144 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',subGroup=' . $subGroup . ',hash=' . $hash . ' - Calling addEntry() ...');
146 // Now add it to the group list and hash it
147 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',this->listGroups[' . $groupName . ']=' . $this->listGroups[$groupName]->__toString());
148 //$this->listGroups[$groupName]->addEntry($subGroup, $hash);
150 // Add the hash to the index
151 array_push($this->listIndex, $hash);
153 // Add the instance itself to the list
154 $this->listEntries[$hash] = $visitableInstance;
157 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',subGroup=' . $subGroup . ' - EXIT!');
161 * Gets an array from given list
163 * @param $list The requested list
164 * @return $array The requested array
165 * @throws NoListGroupException If the given group is not found
167 public final function getArrayFromList ($list) {
169 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',list[' . gettype($list) . ']=' . $list . ' - CALLED!');
171 // Is the group there?
172 if ((!is_null($list)) && (!$this->isGroupSet($list))) {
173 // Throw the exception here
174 throw new NoListGroupException(array($this, $list), self::EXCEPTION_GROUP_NOT_FOUND);
180 // Is there another list?
181 if (!is_null($list)) {
182 // Then get it as well
183 $array = $this->listGroups[$list]->getArrayFromList(NULL);
186 // Walk through all entries
187 foreach ($this->listIndex as $hash) {
189 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: hash=' . $hash);
191 // Is the list entry set?
192 if ($this->isHashValid($hash)) {
194 array_push($array, $this->listEntries[$hash]);
195 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: hash=' . $hash . ',array(' . count($array) . ')=' . print_r($array, TRUE) . ' - ADDED!');
200 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',list[' . gettype($list) . ']=' . $list . ',array()=' . count($array) . ' - EXIT!');
207 * Adds the given entry to list group
209 * @param $groupName Group to add instance to
210 * @param $entry An entry of any type
212 * @throws NoListGroupException If the given group is not found
214 public function addEntry ($groupName, $entry) {
216 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ' - CALLED!');
218 // Is the group already added?
219 if (!$this->isGroupSet($groupName)) {
220 // Throw the exception here
221 throw new NoListGroupException(array($this, $groupName), self::EXCEPTION_GROUP_NOT_FOUND);
225 $hash = $this->generateHash($groupName, $groupName, $entry);
228 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',entry=' . print_r($entry, TRUE) . ', hash=' . $hash);
230 // Add the hash to the index
231 array_push($this->listIndex, $hash);
234 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',listEntries()=' . count($this->listEntries));
236 // Now add the entry to the list
237 $this->listEntries[$hash] = $entry;
240 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',listEntries()=' . count($this->listEntries) . ' - EXIT!');
244 * Removes given entry from the list group
246 * @param $groupName Group where we should remove the entry from
247 * @param $entry The entry we should remove
249 * @throws NoListGroupException If the given group is not found
251 public function removeEntry ($groupName, $entry) {
253 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ' - CALLED!');
255 // Is the group already added?
256 if (!$this->isGroupSet($groupName)) {
257 // Throw the exception here
258 throw new NoListGroupException(array($this, $groupName), self::EXCEPTION_GROUP_NOT_FOUND);
262 $hash = $this->generateHash($groupName, $groupName, $entry);
265 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',entry=' . $entry . ', hash=' . $hash);
267 // Remove it from the list ...
268 unset($this->listEntries[$hash]);
270 // ... and hash list as well
271 unset($this->listIndex[array_search($hash, $this->listIndex)]);
274 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ' - EXIT!');
278 * Generates a hash from given group, sub group and entry
280 * @param $groupName Group to add instance to
281 * @param $subGroup Sub group to add instance to
282 * @param $entry An entry of any type
283 * @return $hash The generated
285 private function generateHash ($groupName, $subGroup, $entry) {
286 // Created entry, 'null' is default
289 // Determine type of entry
290 if (is_null($entry)) {
292 } elseif ($entry instanceof FrameworkInterface) {
293 // Own instance detected
294 $entry2 = $entry->hashCode();
295 } elseif ((is_int($entry)) || (is_float($entry)) || (is_resource($entry))) {
296 // Integer/float/resource detected
297 $entry2 = gettype($entry) . ':' . $entry;
298 } elseif (is_string($entry)) {
300 $entry2 = crc32($entry) . ':' . strlen($entry);
301 } elseif ((is_array($entry)) && (isset($entry['id']))) {
302 // Supported array found
303 $entry2 = crc32($entry['id']) . ':' . count($entry);
304 } elseif ((is_array($entry)) && (isset($entry[BasePool::SOCKET_ARRAY_RESOURCE])) && (isset($entry[BasePool::SOCKET_ARRAY_CONN_TYPE]))) {
305 // Is a socket resource array
306 $entry2 = crc32($entry[BasePool::SOCKET_ARRAY_RESOURCE] . ':' . $entry[BasePool::SOCKET_ARRAY_CONN_TYPE]);
308 // Unsupported type detected
309 self::createDebugInstance(__CLASS__)->debugOutput('BASE-LIST[' . __METHOD__ . ':' . __LINE__ . ']: Entry type ' . gettype($entry) . ' is unsupported.');
311 // @TODO Extend this somehow?
312 $entry2 = gettype($entry);
315 // Construct string which we shall hash
316 $hashString = $groupName . ':' . $subGroup . ':' . $entry2;
318 // Hash it with fastest hasher
319 $hash = crc32($hashString);
326 * Clears an array of groups, all are being checked for existence
328 * @param $groupNames An array with existing list groups
331 protected function clearGroups (array $groupNames) {
332 // Walk through all groups
333 foreach ($groupNames as $groupName) {
335 $this->clearGroup($groupName);
340 * Clears a single group by resetting it to its initial state (empty array)
342 * @param $groupName Name of an existing group to clear
345 protected function clearGroup ($groupName) {
346 // Does this group exist?
347 if (!$this->isGroupSet($groupName)) {
348 // Throw the exception here
349 throw new NoListGroupException(array($this, $groupName), self::EXCEPTION_GROUP_NOT_FOUND);
352 // Then clear this group list
353 $this->listGroups[$groupName]->clearList();
356 $this->listIndex = array();
357 $this->listEntries = array();
361 * Counts all entries in this list
363 * @return $count All entries in this list
365 public final function count () {
366 return count($this->listIndex);
370 * Checks whether the given hash is valid
372 * @param $hash The hash we should validate
373 * @return $isValid Whether the given hash is valid
375 public final function isHashValid ($hash) {
377 $isValid = ((in_array($hash, $this->listIndex)) && (isset($this->listEntries[$hash])));
384 * Getter for hash from given hash index
386 * @param $hashIndex Index holding the hash
387 * @return $hash The hash
389 public final function getHash ($hashIndex) {
391 $hash = $this->listIndex[$hashIndex];
398 * Gets an entry from given hash index
400 * @param $hashIndex The hash index to resolve the mapped entry
401 * @return $entry Solved entry from list
402 * @throws InvalidListHashException If the solved hash index is invalid
404 public function getEntry ($hashIndex) {
405 // Get the hash value
406 $hash = $this->getHash($hashIndex);
408 // Is the hash valid?
409 if (!$this->isHashValid($hash)) {
410 // Throw an exception here
411 throw new InvalidListHashException(array($this, $hash, $hashIndex), self::EXCEPTION_INVALID_HASH);
414 // Now copy the entry
415 $entry = $this->listEntries[$hash];
422 * Gets a full array from given group name
424 * @param $groupName The group name to get a list for
425 * @return $entries The array with all entries
426 * @throws NoListGroupException If the specified group is invalid
428 public function getArrayFromProtocolInstance ($groupName) {
429 // Is the group valid?
430 if (!$this->isGroupSet($groupName)) {
431 // Throw the exception here
432 throw new NoListGroupException(array($this, $groupName), self::EXCEPTION_GROUP_NOT_FOUND);
435 // Init the entries' array
439 $iteratorInstance = $this->listGroups[$groupName]->getIterator();
441 // Rewind the iterator for this round
442 $iteratorInstance->rewind();
444 // Go through all entries
445 while ($iteratorInstance->valid()) {
447 $entryIndex = $iteratorInstance->key();
449 // ... and the final entry which is the stored instance
450 $entry = $this->getEntry($entryIndex);
453 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('LIST: Adding entry ' . $entry . ' ...');
455 // Add it to the list
456 $entries[$iteratorInstance->current()] = $entry;
459 $iteratorInstance->next();
467 * Updates the given entry by hash with given array
469 * @param $hash Hash for this entry
470 * @param $entryArray Array with entry we should update
472 * @throws InvalidListHashException If the solved hash index is invalid
474 public function updateCurrentEntryByHash ($hash, array $entryArray) {
475 // Is the hash valid?
476 if (!$this->isHashValid($hash)) {
477 // Throw an exception here, hashIndex is unknown at this point
478 throw new InvalidListHashException(array($this, $hash, -999), self::EXCEPTION_INVALID_HASH);
482 $this->listEntries[$hash] = $entryArray;