5 * @author Roland Haeder <webmaster@shipsimu.org>
7 * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2012 Hub 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 BaseHubSystem 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 //* DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(__METHOD__.': '.$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 //* DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(__METHOD__.': '.$groupName . ' - START');
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 //* DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(__METHOD__.': '.$groupName . ' - FINISHED');
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) {
119 //* DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(__METHOD__.': '.$groupName . '/' . $subGroup . ' - START');
120 // Is the group there?
121 if (!$this->isGroupSet($groupName)) {
122 // Throw the exception here
123 throw new NoListGroupException(array($this, $groupName), self::EXCEPTION_GROUP_NOT_FOUND);
126 // Is the sub group there?
127 if (!$this->listGroups[$groupName]->isGroupSet($subGroup)) {
128 // Automatically add it
129 $this->listGroups[$groupName]->addGroup($subGroup);
133 $hash = $this->generateHash($groupName, $subGroup, $visitableInstance);
135 // Now add it to the group list and hash it
136 $this->listGroups[$groupName]->addEntry($subGroup, $hash);
138 // Add the hash to the index
139 array_push($this->listIndex, $hash);
141 // Add the instance itself to the list
142 $this->listEntries[$hash] = $visitableInstance;
143 //* DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(__METHOD__.': '.$groupName . '/' . $subGroup . ' - FINISHED');
147 * Gets an array from given list
149 * @param $list The requested list
150 * @return $array The requested array
151 * @throws NoListGroupException If the given group is not found
153 public final function getArrayFromList ($list) {
154 // Is the group there?
155 if ((!is_null($list)) && (!$this->isGroupSet($list))) {
156 // Throw the exception here
157 throw new NoListGroupException(array($this, $list), self::EXCEPTION_GROUP_NOT_FOUND);
163 // Is there another list?
164 if (!is_null($list)) {
165 // Then get it as well
166 $array = $this->listGroups[$list]->getArrayFromList(NULL);
169 // Walk through all entries
170 foreach ($this->listIndex as $hash) {
171 //* DEBUG: */ print __METHOD__.':hash='.$hash.chr(10);
172 // Is the list entry set?
173 if ($this->isHashValid($hash)) {
175 array_push($array, $this->listEntries[$hash]);
176 //* DEBUG: */ print __METHOD__.": ADDED!\n";
185 * Adds the given entry to list group
187 * @param $groupName Group to add instance to
188 * @param $entry An entry of any type
190 * @throws NoListGroupException If the given group is not found
192 public function addEntry ($groupName, $entry) {
193 //* DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(__METHOD__.'('.$this->__toString().'): '.$groupName . ' - START');
194 // Is the group already added?
195 if (!$this->isGroupSet($groupName)) {
196 // Throw the exception here
197 throw new NoListGroupException(array($this, $groupName), self::EXCEPTION_GROUP_NOT_FOUND);
201 $hash = $this->generateHash($groupName, $groupName, $entry);
202 //* DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: groupName=' . $groupName . ', entry=' . $entry . ', hash=' . $hash);
204 // Add the hash to the index
205 array_push($this->listIndex, $hash);
206 //* DEBUG: */ print $groupName.'/'.$this->count().chr(10);
208 // Now add the entry to the list
209 $this->listEntries[$hash] = $entry;
210 //* DEBUG: */ print $groupName.'/'.count($this->listEntries).chr(10);
211 //* DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(__METHOD__.'('.$this->__toString().'): '.$groupName . ' - FINISHED');
215 * Removes given entry from the list group
217 * @param $groupName Group where we should remove the entry from
218 * @param $entry The entry we should remove
220 * @throws NoListGroupException If the given group is not found
222 public function removeEntry ($groupName, $entry) {
223 //* DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(__METHOD__.'('.$this->__toString().'): '.$groupName . ' - START');
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);
232 //* DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: groupName=' . $groupName . ', entry=' . $entry . ', hash=' . $hash);
234 // Remove it from the list ...
235 unset($this->listEntries[$hash]);
237 // ... and hash list as well
238 unset($this->listIndex[array_search($hash, $this->listIndex)]);
239 //* DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput(__METHOD__.'('.$this->__toString().'): '.$groupName . ' - FINISHED');
243 * Generates a hash from given group, sub group and entry
245 * @param $groupName Group to add instance to
246 * @param $subGroup Sub group to add instance to
247 * @param $entry An entry of any type
248 * @return $hash The generated
250 private function generateHash ($groupName, $subGroup, $entry) {
251 // Created entry, 'null' is default
254 // Determine type of entry
255 if (is_null($entry)) {
257 } elseif ($entry instanceof FrameworkInterface) {
258 // Own instance detected
259 $entry2 = $entry->hashCode();
260 } elseif ((is_int($entry)) || (is_float($entry)) || (is_resource($entry))) {
261 // Integer/float/resource detected
262 $entry2 = gettype($entry) . ':' . $entry;
263 } elseif (is_string($entry)) {
265 $entry2 = crc32($entry) . ':' . strlen($entry);
266 } elseif ((is_array($entry)) && (isset($entry['id']))) {
267 // Supported array found
268 $entry2 = crc32($entry['id']) . ':' . count($entry);
269 } elseif ((is_array($entry)) && (isset($entry[BasePool::SOCKET_ARRAY_RESOURCE])) && (isset($entry[BasePool::SOCKET_ARRAY_CONN_TYPE]))) {
270 // Is a socket resource array
271 $entry2 = crc32($entry[BasePool::SOCKET_ARRAY_RESOURCE] . ':' . $entry[BasePool::SOCKET_ARRAY_CONN_TYPE]);
273 // Unsupported type detected
274 self::createDebugInstance(__CLASS__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: Entry type ' . gettype($entry) . ' is unsupported.');
276 // @TODO Extend this somehow?
277 $entry2 = gettype($entry);
280 // Construct string which we shall hash
281 $hashString = $groupName . ':' . $subGroup . ':' . $entry2;
283 // Hash it with fastest hasher
284 $hash = crc32($hashString);
291 * Clears an array of groups, all are being checked for existence
293 * @param $groupNames An array with existing list groups
296 protected function clearGroups (array $groupNames) {
297 // Walk through all groups
298 foreach ($groupNames as $groupName) {
300 $this->clearGroup($groupName);
305 * Clears a single group by resetting it to its initial state (empty array)
307 * @param $groupName Name of an existing group to clear
310 protected function clearGroup ($groupName) {
311 // Does this group exist?
312 if (!$this->isGroupSet($groupName)) {
313 // Throw the exception here
314 throw new NoListGroupException(array($this, $groupName), self::EXCEPTION_GROUP_NOT_FOUND);
317 // Then clear this group list
318 $this->listGroups[$groupName]->clearList();
321 $this->listIndex = array();
322 $this->listEntries = array();
326 * Counts all entries in this list
328 * @return $count All entries in this list
330 public final function count () {
331 return count($this->listIndex);
335 * Checks whether the given hash is valid
337 * @param $hash The hash we should validate
338 * @return $isValid Whether the given hash is valid
340 public final function isHashValid ($hash) {
342 $isValid = ((in_array($hash, $this->listIndex)) && (isset($this->listEntries[$hash])));
349 * Getter for hash from given hash index
351 * @param $hashIndex Index holding the hash
352 * @return $hash The hash
354 public final function getHash ($hashIndex) {
356 $hash = $this->listIndex[$hashIndex];
363 * Gets an entry from given hash index
365 * @param $hashIndex The hash index to resolve the mapped entry
366 * @return $entry Solved entry from list
367 * @throws InvalidListHashException If the solved hash index is invalid
369 public function getEntry ($hashIndex) {
370 // Get the hash value
371 $hash = $this->getHash($hashIndex);
373 // Is the hash valid?
374 if (!$this->isHashValid($hash)) {
375 // Throw an exception here
376 throw new InvalidListHashException(array($this, $hash, $hashIndex), self::EXCEPTION_INVALID_HASH);
379 // Now copy the entry
380 $entry = $this->listEntries[$hash];
387 * Gets a full array from given group name
389 * @param $groupName The group name to get a list for
390 * @return $entries The array with all entries
391 * @throws NoListGroupException If the specified group is invalid
393 public function getArrayFromGroup ($groupName) {
394 // Is the group valid?
395 if (!$this->isGroupSet($groupName)) {
396 // Throw the exception here
397 throw new NoListGroupException(array($this, $groupName), self::EXCEPTION_GROUP_NOT_FOUND);
400 // Init the entries' array
404 $iteratorInstance = $this->listGroups[$groupName]->getIterator();
406 // Rewind the iterator for this round
407 $iteratorInstance->rewind();
409 // Go through all entries
410 while ($iteratorInstance->valid()) {
412 $entryIndex = $iteratorInstance->key();
414 // ... and the final entry which is the stored instance
415 $entry = $this->getEntry($entryIndex);
418 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('LIST: Adding entry ' . $entry . ' ...');
420 // Add it to the list
421 $entries[$iteratorInstance->current()] = $entry;
424 $iteratorInstance->next();
432 * Updates the given entry by hash with given array
434 * @param $hash Hash for this entry
435 * @param $entryArray Array with entry we should update
437 * @throws InvalidListHashException If the solved hash index is invalid
439 public function updateCurrentEntryByHash ($hash, array $entryArray) {
440 // Is the hash valid?
441 if (!$this->isHashValid($hash)) {
442 // Throw an exception here, hashIndex is unknown at this point
443 throw new InvalidListHashException(array($this, $hash, -999), self::EXCEPTION_INVALID_HASH);
447 $this->listEntries[$hash] = $entryArray;