3 namespace CoreFramework\Lists;
5 // Import framework stuff
6 use CoreFramework\Factory\ObjectFactory;
7 use CoreFramework\Generic\FrameworkInterface;
8 use CoreFramework\Object\BaseFrameworkSystem;
11 * A general list class
13 * @author Roland Haeder <webmaster@shipsimu.org>
15 * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Core Developer Team
16 * @license GNU GPL 3.0 or any newer version
17 * @link http://www.shipsimu.org
19 * This program is free software: you can redistribute it and/or modify
20 * it under the terms of the GNU General Public License as published by
21 * the Free Software Foundation, either version 3 of the License, or
22 * (at your option) any later version.
24 * This program is distributed in the hope that it will be useful,
25 * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 * GNU General Public License for more details.
29 * You should have received a copy of the GNU General Public License
30 * along with this program. If not, see <http://www.gnu.org/licenses/>.
32 class BaseList extends BaseFrameworkSystem implements IteratorAggregate, Countable {
33 // Exception constants
34 const EXCEPTION_GROUP_ALREADY_ADDED = 0xf20;
35 const EXCEPTION_GROUP_NOT_FOUND = 0xf21;
36 const EXCEPTION_INVALID_HASH = 0xf22;
41 private $listGroups = array();
46 private $listEntries = array();
51 private $listIndex = array();
54 * Protected constructor
56 * @param $className Name of the class
59 protected function __construct ($className) {
60 // Call parent constructor
61 parent::__construct($className);
65 * Getter for iterator instance from this list
67 * @return $iteratorInstance An instance of a Iterator class
69 public function getIterator () {
70 // Get iterator from here
71 $iteratorInstance = $this->getIteratorInstance();
73 // Is the instance set?
74 if (is_null($iteratorInstance)) {
75 // Prepare a default iterator
76 $iteratorInstance = ObjectFactory::createObjectByConfiguredName('default_iterator_class', array($this));
79 $this->setIteratorInstance($iteratorInstance);
83 return $iteratorInstance;
87 * Checks whether the given group is set
89 * @param $groupName Group to check if found in list
90 * @return $isset Whether the group is valid
92 public function isGroupSet ($groupName) {
93 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName);
94 return isset($this->listGroups[$groupName]);
98 * Adds the given group or if already added issues a ListGroupAlreadyAddedException
100 * @param $groupName Group to add
102 * @throws ListGroupAlreadyAddedException If the given group is already added
104 public function addGroup ($groupName) {
105 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ' - CALLED!');
106 // Is the group already added?
107 if ($this->isGroupSet($groupName)) {
108 // Throw the exception here
109 throw new ListGroupAlreadyAddedException(array($this, $groupName), self::EXCEPTION_GROUP_ALREADY_ADDED);
112 // Add the group which is a simple array
113 $this->listGroups[$groupName] = ObjectFactory::createObjectByConfiguredName('list_group_class');
114 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ' - EXIT!');
118 * Adds the given instance to list group and sub group
120 * @param $groupName Group to add instance to
121 * @param $subGroup Sub group to add instance to
122 * @param $visitableInstance An instance of Visitable
124 * @throws NoListGroupException If the given group is not found
126 public function addInstance ($groupName, $subGroup, Visitable $visitableInstance) {
128 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',subGroup=' . $subGroup . ',visitableInstance=' . $visitableInstance->__toString() . ' - CALLED!');
130 // Is the group there?
131 if (!$this->isGroupSet($groupName)) {
132 // Throw the exception here
133 throw new NoListGroupException(array($this, $groupName), self::EXCEPTION_GROUP_NOT_FOUND);
136 // Is the sub group there?
137 if (!$this->listGroups[$groupName]->isGroupSet($subGroup)) {
138 // Automatically add it
139 $this->listGroups[$groupName]->addGroup($subGroup);
143 $hash = $this->generateHash($groupName, $subGroup, $visitableInstance);
146 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',subGroup=' . $subGroup . ',hash=' . $hash . ' - Calling addEntry() ...');
148 // Now add it to the group list and hash it
149 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',this->listGroups[' . $groupName . ']=' . $this->listGroups[$groupName]->__toString());
150 //$this->listGroups[$groupName]->addEntry($subGroup, $hash);
152 // Add the hash to the index
153 array_push($this->listIndex, $hash);
155 // Add the instance itself to the list
156 $this->listEntries[$hash] = $visitableInstance;
159 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',subGroup=' . $subGroup . ' - EXIT!');
163 * Gets an array from given list
165 * @param $list The requested list
166 * @return $array The requested array
167 * @throws NoListGroupException If the given group is not found
169 public final function getArrayFromList ($list) {
171 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',list[' . gettype($list) . ']=' . $list . ' - CALLED!');
173 // Is the group there?
174 if ((!is_null($list)) && (!$this->isGroupSet($list))) {
175 // Throw the exception here
176 throw new NoListGroupException(array($this, $list), self::EXCEPTION_GROUP_NOT_FOUND);
182 // Is there another list?
183 if (!is_null($list)) {
184 // Then get it as well
185 $array = $this->listGroups[$list]->getArrayFromList(NULL);
188 // Walk through all entries
189 foreach ($this->listIndex as $hash) {
191 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: hash=' . $hash);
193 // Is the list entry set?
194 if ($this->isHashValid($hash)) {
196 array_push($array, $this->listEntries[$hash]);
197 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: hash=' . $hash . ',array(' . count($array) . ')=' . print_r($array, TRUE) . ' - ADDED!');
202 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',list[' . gettype($list) . ']=' . $list . ',array()=' . count($array) . ' - EXIT!');
209 * Adds the given entry to list group
211 * @param $groupName Group to add instance to
212 * @param $entry An entry of any type
214 * @throws NoListGroupException If the given group is not found
216 public function addEntry ($groupName, $entry) {
218 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ' - CALLED!');
220 // Is the group already added?
221 if (!$this->isGroupSet($groupName)) {
222 // Throw the exception here
223 throw new NoListGroupException(array($this, $groupName), self::EXCEPTION_GROUP_NOT_FOUND);
227 $hash = $this->generateHash($groupName, $groupName, $entry);
230 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',entry=' . print_r($entry, TRUE) . ', hash=' . $hash);
232 // Add the hash to the index
233 array_push($this->listIndex, $hash);
236 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',listEntries()=' . count($this->listEntries));
238 // Now add the entry to the list
239 $this->listEntries[$hash] = $entry;
242 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',listEntries()=' . count($this->listEntries) . ' - EXIT!');
246 * Removes given entry from the list group
248 * @param $groupName Group where we should remove the entry from
249 * @param $entry The entry we should remove
251 * @throws NoListGroupException If the given group is not found
253 public function removeEntry ($groupName, $entry) {
255 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ' - CALLED!');
257 // Is the group already added?
258 if (!$this->isGroupSet($groupName)) {
259 // Throw the exception here
260 throw new NoListGroupException(array($this, $groupName), self::EXCEPTION_GROUP_NOT_FOUND);
264 $hash = $this->generateHash($groupName, $groupName, $entry);
267 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',entry=' . $entry . ', hash=' . $hash);
269 // Remove it from the list ...
270 unset($this->listEntries[$hash]);
272 // ... and hash list as well
273 unset($this->listIndex[array_search($hash, $this->listIndex)]);
276 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ' - EXIT!');
280 * Generates a hash from given group, sub group and entry
282 * @param $groupName Group to add instance to
283 * @param $subGroup Sub group to add instance to
284 * @param $entry An entry of any type
285 * @return $hash The generated
287 private function generateHash ($groupName, $subGroup, $entry) {
288 // Created entry, 'null' is default
291 // Determine type of entry
292 if (is_null($entry)) {
294 } elseif ($entry instanceof FrameworkInterface) {
295 // Own instance detected
296 $entry2 = $entry->hashCode();
297 } elseif ((is_int($entry)) || (is_float($entry)) || (is_resource($entry))) {
298 // Integer/float/resource detected
299 $entry2 = gettype($entry) . ':' . $entry;
300 } elseif (is_string($entry)) {
302 $entry2 = crc32($entry) . ':' . strlen($entry);
303 } elseif ((is_array($entry)) && (isset($entry['id']))) {
304 // Supported array found
305 $entry2 = crc32($entry['id']) . ':' . count($entry);
306 } elseif ((is_array($entry)) && (isset($entry[BasePool::SOCKET_ARRAY_RESOURCE])) && (isset($entry[BasePool::SOCKET_ARRAY_CONN_TYPE]))) {
307 // Is a socket resource array
308 $entry2 = crc32($entry[BasePool::SOCKET_ARRAY_RESOURCE] . ':' . $entry[BasePool::SOCKET_ARRAY_CONN_TYPE]);
310 // Unsupported type detected
311 self::createDebugInstance(__CLASS__)->debugOutput('BASE-LIST[' . __METHOD__ . ':' . __LINE__ . ']: Entry type ' . gettype($entry) . ' is unsupported.');
313 // @TODO Extend this somehow?
314 $entry2 = gettype($entry);
317 // Construct string which we shall hash
318 $hashString = $groupName . ':' . $subGroup . ':' . $entry2;
320 // Hash it with fastest hasher
321 $hash = crc32($hashString);
328 * Clears an array of groups, all are being checked for existence
330 * @param $groupNames An array with existing list groups
333 protected function clearGroups (array $groupNames) {
334 // Walk through all groups
335 foreach ($groupNames as $groupName) {
337 $this->clearGroup($groupName);
342 * Clears a single group by resetting it to its initial state (empty array)
344 * @param $groupName Name of an existing group to clear
347 protected function clearGroup ($groupName) {
348 // Does this group exist?
349 if (!$this->isGroupSet($groupName)) {
350 // Throw the exception here
351 throw new NoListGroupException(array($this, $groupName), self::EXCEPTION_GROUP_NOT_FOUND);
354 // Then clear this group list
355 $this->listGroups[$groupName]->clearList();
358 $this->listIndex = array();
359 $this->listEntries = array();
363 * Counts all entries in this list
365 * @return $count All entries in this list
367 public final function count () {
368 return count($this->listIndex);
372 * Checks whether the given hash is valid
374 * @param $hash The hash we should validate
375 * @return $isValid Whether the given hash is valid
377 public final function isHashValid ($hash) {
379 $isValid = ((in_array($hash, $this->listIndex)) && (isset($this->listEntries[$hash])));
386 * Getter for hash from given hash index
388 * @param $hashIndex Index holding the hash
389 * @return $hash The hash
391 public final function getHash ($hashIndex) {
393 $hash = $this->listIndex[$hashIndex];
400 * Gets an entry from given hash index
402 * @param $hashIndex The hash index to resolve the mapped entry
403 * @return $entry Solved entry from list
404 * @throws InvalidListHashException If the solved hash index is invalid
406 public function getEntry ($hashIndex) {
407 // Get the hash value
408 $hash = $this->getHash($hashIndex);
410 // Is the hash valid?
411 if (!$this->isHashValid($hash)) {
412 // Throw an exception here
413 throw new InvalidListHashException(array($this, $hash, $hashIndex), self::EXCEPTION_INVALID_HASH);
416 // Now copy the entry
417 $entry = $this->listEntries[$hash];
424 * Gets a full array from given group name
426 * @param $groupName The group name to get a list for
427 * @return $entries The array with all entries
428 * @throws NoListGroupException If the specified group is invalid
430 public function getArrayFromProtocolInstance ($groupName) {
431 // Is the group valid?
432 if (!$this->isGroupSet($groupName)) {
433 // Throw the exception here
434 throw new NoListGroupException(array($this, $groupName), self::EXCEPTION_GROUP_NOT_FOUND);
437 // Init the entries' array
441 $iteratorInstance = $this->listGroups[$groupName]->getIterator();
443 // Rewind the iterator for this round
444 $iteratorInstance->rewind();
446 // Go through all entries
447 while ($iteratorInstance->valid()) {
449 $entryIndex = $iteratorInstance->key();
451 // ... and the final entry which is the stored instance
452 $entry = $this->getEntry($entryIndex);
455 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('LIST: Adding entry ' . $entry . ' ...');
457 // Add it to the list
458 $entries[$iteratorInstance->current()] = $entry;
461 $iteratorInstance->next();
469 * Updates the given entry by hash with given array
471 * @param $hash Hash for this entry
472 * @param $entryArray Array with entry we should update
474 * @throws InvalidListHashException If the solved hash index is invalid
476 public function updateCurrentEntryByHash ($hash, array $entryArray) {
477 // Is the hash valid?
478 if (!$this->isHashValid($hash)) {
479 // Throw an exception here, hashIndex is unknown at this point
480 throw new InvalidListHashException(array($this, $hash, -999), self::EXCEPTION_INVALID_HASH);
484 $this->listEntries[$hash] = $entryArray;