3 namespace CoreFramework\Lists;
5 // Import framework stuff
6 use CoreFramework\Factory\ObjectFactory;
7 use CoreFramework\Generic\FrameworkInterface;
8 use CoreFramework\Object\BaseFrameworkSystem;
11 use \IteratorAggregate;
15 * A general list class
17 * @author Roland Haeder <webmaster@shipsimu.org>
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
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.
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.
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/>.
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;
45 private $listGroups = array();
50 private $listEntries = array();
55 private $listIndex = array();
58 * Protected constructor
60 * @param $className Name of the class
63 protected function __construct ($className) {
64 // Call parent constructor
65 parent::__construct($className);
69 * Getter for iterator instance from this list
71 * @return $iteratorInstance An instance of a Iterator class
73 public function getIterator () {
74 // Get iterator from here
75 $iteratorInstance = $this->getIteratorInstance();
77 // Is the instance set?
78 if (is_null($iteratorInstance)) {
79 // Prepare a default iterator
80 $iteratorInstance = ObjectFactory::createObjectByConfiguredName('default_iterator_class', array($this));
83 $this->setIteratorInstance($iteratorInstance);
87 return $iteratorInstance;
91 * Checks whether the given group is set
93 * @param $groupName Group to check if found in list
94 * @return $isset Whether the group is valid
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]);
102 * Adds the given group or if already added issues a ListGroupAlreadyAddedException
104 * @param $groupName Group to add
106 * @throws ListGroupAlreadyAddedException If the given group is already added
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);
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!');
122 * Adds the given instance to list group and sub group
124 * @param $groupName Group to add instance to
125 * @param $subGroup Sub group to add instance to
126 * @param $visitableInstance An instance of Visitable
128 * @throws NoListGroupException If the given group is not found
130 public function addInstance ($groupName, $subGroup, Visitable $visitableInstance) {
132 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',subGroup=' . $subGroup . ',visitableInstance=' . $visitableInstance->__toString() . ' - CALLED!');
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);
140 // Is the sub group there?
141 if (!$this->listGroups[$groupName]->isGroupSet($subGroup)) {
142 // Automatically add it
143 $this->listGroups[$groupName]->addGroup($subGroup);
147 $hash = $this->generateHash($groupName, $subGroup, $visitableInstance);
150 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',subGroup=' . $subGroup . ',hash=' . $hash . ' - Calling addEntry() ...');
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);
156 // Add the hash to the index
157 array_push($this->listIndex, $hash);
159 // Add the instance itself to the list
160 $this->listEntries[$hash] = $visitableInstance;
163 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',subGroup=' . $subGroup . ' - EXIT!');
167 * Gets an array from given list
169 * @param $list The requested list
170 * @return $array The requested array
171 * @throws NoListGroupException If the given group is not found
173 public final function getArrayFromList ($list) {
175 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',list[' . gettype($list) . ']=' . $list . ' - CALLED!');
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);
186 // Is there another list?
187 if (!is_null($list)) {
188 // Then get it as well
189 $array = $this->listGroups[$list]->getArrayFromList(NULL);
192 // Walk through all entries
193 foreach ($this->listIndex as $hash) {
195 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: hash=' . $hash);
197 // Is the list entry set?
198 if ($this->isHashValid($hash)) {
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!');
206 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',list[' . gettype($list) . ']=' . $list . ',array()=' . count($array) . ' - EXIT!');
213 * Adds the given entry to list group
215 * @param $groupName Group to add instance to
216 * @param $entry An entry of any type
218 * @throws NoListGroupException If the given group is not found
220 public function addEntry ($groupName, $entry) {
222 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ' - CALLED!');
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);
231 $hash = $this->generateHash($groupName, $groupName, $entry);
234 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',entry=' . print_r($entry, TRUE) . ', hash=' . $hash);
236 // Add the hash to the index
237 array_push($this->listIndex, $hash);
240 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',listEntries()=' . count($this->listEntries));
242 // Now add the entry to the list
243 $this->listEntries[$hash] = $entry;
246 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',listEntries()=' . count($this->listEntries) . ' - EXIT!');
250 * Removes given entry from the list group
252 * @param $groupName Group where we should remove the entry from
253 * @param $entry The entry we should remove
255 * @throws NoListGroupException If the given group is not found
257 public function removeEntry ($groupName, $entry) {
259 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ' - CALLED!');
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);
268 $hash = $this->generateHash($groupName, $groupName, $entry);
271 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',entry=' . $entry . ', hash=' . $hash);
273 // Remove it from the list ...
274 unset($this->listEntries[$hash]);
276 // ... and hash list as well
277 unset($this->listIndex[array_search($hash, $this->listIndex)]);
280 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ' - EXIT!');
284 * Generates a hash from given group, sub group and entry
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
291 private function generateHash ($groupName, $subGroup, $entry) {
292 // Created entry, 'null' is default
295 // Determine type of entry
296 if (is_null($entry)) {
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)) {
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]);
314 // Unsupported type detected
315 self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-LIST[' . __METHOD__ . ':' . __LINE__ . ']: Entry type ' . gettype($entry) . ' is unsupported.');
317 // @TODO Extend this somehow?
318 $entry2 = gettype($entry);
321 // Construct string which we shall hash
322 $hashString = $groupName . ':' . $subGroup . ':' . $entry2;
324 // Hash it with fastest hasher
325 $hash = crc32($hashString);
332 * Clears an array of groups, all are being checked for existence
334 * @param $groupNames An array with existing list groups
337 protected function clearGroups (array $groupNames) {
338 // Walk through all groups
339 foreach ($groupNames as $groupName) {
341 $this->clearGroup($groupName);
346 * Clears a single group by resetting it to its initial state (empty array)
348 * @param $groupName Name of an existing group to clear
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);
358 // Then clear this group list
359 $this->listGroups[$groupName]->clearList();
362 $this->listIndex = array();
363 $this->listEntries = array();
367 * Counts all entries in this list
369 * @return $count All entries in this list
371 public final function count () {
372 return count($this->listIndex);
376 * Checks whether the given hash is valid
378 * @param $hash The hash we should validate
379 * @return $isValid Whether the given hash is valid
381 public final function isHashValid ($hash) {
383 $isValid = ((in_array($hash, $this->listIndex)) && (isset($this->listEntries[$hash])));
390 * Getter for hash from given hash index
392 * @param $hashIndex Index holding the hash
393 * @return $hash The hash
395 public final function getHash ($hashIndex) {
397 $hash = $this->listIndex[$hashIndex];
404 * Gets an entry from given hash index
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
410 public function getEntry ($hashIndex) {
411 // Get the hash value
412 $hash = $this->getHash($hashIndex);
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);
420 // Now copy the entry
421 $entry = $this->listEntries[$hash];
428 * Gets a full array from given group name
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
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);
441 // Init the entries' array
445 $iteratorInstance = $this->listGroups[$groupName]->getIterator();
447 // Rewind the iterator for this round
448 $iteratorInstance->rewind();
450 // Go through all entries
451 while ($iteratorInstance->valid()) {
453 $entryIndex = $iteratorInstance->key();
455 // ... and the final entry which is the stored instance
456 $entry = $this->getEntry($entryIndex);
459 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('LIST: Adding entry ' . $entry . ' ...');
461 // Add it to the list
462 $entries[$iteratorInstance->current()] = $entry;
465 $iteratorInstance->next();
473 * Updates the given entry by hash with given array
475 * @param $hash Hash for this entry
476 * @param $entryArray Array with entry we should update
478 * @throws InvalidListHashException If the solved hash index is invalid
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);
488 $this->listEntries[$hash] = $entryArray;