5 * @author Roland Haeder <webmaster@ship-simu.org>
7 * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2011 Hub Developer Team
8 * @license GNU GPL 3.0 or any newer version
9 * @link http://www.ship-simu.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: */ $this->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: */ $this->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: */ $this->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 $instance An instance of Visitable
116 * @throws NoListGroupException If the given group is not found
118 public function addInstance ($groupName, $subGroup, Visitable $instance) {
119 //* DEBUG: */ $this->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, $instance);
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 $this->listIndex[] = $hash;
141 // Add the instance itself to the list
142 $this->listEntries[$hash] = $instance;
143 //* DEBUG: */ $this->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[] = $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: */ $this->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: */ $this->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: groupName=' . $groupName . ', entry=' . $entry . ', hash=' . $hash);
204 // Add the hash to the index
205 $this->listIndex[] = $hash;
206 //* DEBUG: */ print $groupName.'/'.count($this->listIndex).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: */ $this->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: */ $this->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: */ $this->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: */ $this->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);
270 // Unsupported type detected
271 $this->debugOutut('[' . __METHOD__ . ':' . __LINE__ . ']: entry type ' . gettype($entry) . ' is unsupported.');
273 // @TODO Extend this somehow?
274 $entry2 = gettype($entry);
277 // Construct string which we shall hash
278 $hashString = $groupName . ':' . $subGroup . ':' . $entry2;
280 // Hash it with fastest hasher
281 $hash = crc32($hashString);
288 * Clears an array of groups, all are being checked for existence
290 * @param $groupNames An array with existing list groups
293 protected function clearGroups (array $groupNames) {
294 // Walk through all groups
295 foreach ($groupNames as $groupName) {
297 $this->clearGroup($groupName);
302 * Clears a single group by resetting it to its initial state (empty array)
304 * @param $groupName Name of an existing group to clear
307 protected function clearGroup ($groupName) {
308 // Does this group exist?
309 if (!$this->isGroupSet($groupName)) {
310 // Throw the exception here
311 throw new NoListGroupException(array($this, $groupName), self::EXCEPTION_GROUP_NOT_FOUND);
314 // Then clear this group list
315 $this->listGroups[$groupName]->clearList();
318 $this->listIndex = array();
319 $this->listEntries = array();
323 * Counts all entries in this list
325 * @return $count All entries in this list
327 public final function count () {
328 return count($this->listIndex);
332 * Checks whether the given hash is valid
334 * @param $hash The hash we should validate
335 * @return $isValid Whether the given hash is valid
337 public final function isHashValid ($hash) {
339 $isValid = ((in_array($hash, $this->listIndex)) && (isset($this->listEntries[$hash])));
346 * Getter for hash from given hash index
348 * @param $hashIndex Index holding the hash
349 * @return $hash The hash
351 public final function getHash ($hashIndex) {
353 $hash = $this->listIndex[$hashIndex];
360 * Gets an entry from given hash index
362 * @param $hashIndex The hash index to resolve the mapped entry
363 * @return $entry Solved entry from list
364 * @throws InvalidListHashException If the solved hash index is invalid
366 public function getEntry ($hashIndex) {
367 // Get the hash value
368 $hash = $this->getHash($hashIndex);
370 // Is the hash valid?
371 if (!$this->isHashValid($hash)) {
372 // Throw an exception here
373 throw new InvalidListHashException(array($this, $hash, $hashIndex), self::EXCEPTION_INVALID_HASH);
376 // Now copy the entry
377 $entry = $this->listEntries[$hash];
384 * Gets a full array from given group name
386 * @param $groupName The group name to get a list for
387 * @return $entries The array with all entries
388 * @throws NoListGroupException If the specified group is invalid
390 public function getArrayFromGroup ($groupName) {
391 // Is the group valid?
392 if (!$this->isGroupSet($groupName)) {
393 // Throw the exception here
394 throw new NoListGroupException(array($this, $groupName), self::EXCEPTION_GROUP_NOT_FOUND);
397 // Init the entries' array
401 $iteratorInstance = $this->listGroups[$groupName]->getIterator();
403 // Go through all entries
404 while ($iteratorInstance->valid()) {
406 $entryIndex = $iteratorInstance->key();
408 // ... and the final entry which is the stored instance
409 $entry = $this->getEntry($entryIndex);
411 // Add it to the list
412 $entries[$iteratorInstance->current()] = $entry;
415 $iteratorInstance->next();
423 * Updates the given entry by hash with given array
425 * @param $hash Hash for this entry
426 * @param $entryArray Array with entry we should update
428 * @throws InvalidListHashException If the solved hash index is invalid
430 public function updateCurrentEntryByHash ($hash, array $entryArray) {
431 // Is the hash valid?
432 if (!$this->isHashValid($hash)) {
433 // Throw an exception here, hashIndex is unknown at this point
434 throw new InvalidListHashException(array($this, $hash, -999), self::EXCEPTION_INVALID_HASH);
438 $this->listEntries[$hash] = $entryArray;