5 * @author Roland Haeder <webmaster@ship-simu.org>
7 * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009, 2010 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 // Prepare a default iterator
63 $iteratorInstance = ObjectFactory::createObjectByConfiguredName('default_iterator_class', array($this));
66 return $iteratorInstance;
70 * Checks wether the given group is set
72 * @param $groupName Group to check if found in list
73 * @return $isset Wether the group is valid
75 public function isGroupSet ($groupName) {
76 //* DEBUG: */ $this->debugOutput(__METHOD__.': '.$groupName);
77 return isset($this->listGroups[$groupName]);
81 * Adds the given group or if already added issues a ListGroupAlreadyAddedException
83 * @param $groupName Group to add
85 * @throws ListGroupAlreadyAddedException If the given group is already added
87 public function addGroup ($groupName) {
88 //* DEBUG: */ $this->debugOutput(__METHOD__.': '.$groupName . ' - START');
89 // Is the group already added?
90 if ($this->isGroupSet($groupName)) {
91 // Throw the exception here
92 throw new ListGroupAlreadyAddedException(array($this, $groupName), self::EXCEPTION_GROUP_ALREADY_ADDED);
95 // Add the group which is a simple array
96 $this->listGroups[$groupName] = ObjectFactory::createObjectByConfiguredName('list_group_class');
97 //* DEBUG: */ $this->debugOutput(__METHOD__.': '.$groupName . ' - FINISHED');
101 * Adds the given instance to list group and sub group
103 * @param $groupName Group to add instance to
104 * @param $subGroup Sub group to add instance to
105 * @param $instance An instance of Visitable
107 * @throws NoListGroupException If the given group is not found
109 public function addInstance ($groupName, $subGroup, Visitable $instance) {
110 //* DEBUG: */ $this->debugOutput(__METHOD__.': '.$groupName . '/' . $subGroup . ' - START');
111 // Is the group there?
112 if (!$this->isGroupSet($groupName)) {
113 // Throw the exception here
114 throw new NoListGroupException(array($this, $groupName), self::EXCEPTION_GROUP_NOT_FOUND);
117 // Is the sub group there?
118 if (!$this->listGroups[$groupName]->isGroupSet($subGroup)) {
119 // Automatically add it
120 $this->listGroups[$groupName]->addGroup($subGroup);
124 $hash = $this->generateHash($groupName, $subGroup, $instance);
126 // Now add it to the group list and hash it
127 $this->listGroups[$groupName]->addEntry($subGroup, $hash);
129 // Add the hash to the index
130 $this->listIndex[] = $hash;
132 // Add the instance itself to the list
133 $this->listEntries[$hash] = $instance;
134 //* DEBUG: */ $this->debugOutput(__METHOD__.': '.$groupName . '/' . $subGroup . ' - FINISHED');
138 * Gets an array from given list
140 * @param $list The requested list
141 * @return $array The requested array
142 * @throws NoListGroupException If the given group is not found
144 public final function getArrayFromList ($list) {
145 // Is the group there?
146 if ((!is_null($list)) && (!$this->isGroupSet($list))) {
147 // Throw the exception here
148 throw new NoListGroupException(array($this, $list), self::EXCEPTION_GROUP_NOT_FOUND);
154 // Is there another list?
155 if (!is_null($list)) {
156 // Then get it as well
157 $array = $this->listGroups[$list]->getArrayFromList(null);
160 // Walk through all entries
161 foreach ($this->listIndex as $hash) {
162 //* DEBUG: */ print __METHOD__.':hash='.$hash."\n";
163 // Is the list entry set?
164 if ($this->isHashValid($hash)) {
166 $array[] = $this->listEntries[$hash];
167 //* DEBUG: */ print __METHOD__.": ADDED!\n";
176 * Adds the given entry to list group
178 * @param $groupName Group to add instance to
179 * @param $entry An entry of any type
181 * @throws NoListGroupException If the given group is not found
183 public function addEntry ($groupName, $entry) {
184 //* DEBUG: */ $this->debugOutput(__METHOD__.'('.$this->__toString().'): '.$groupName . ' - START');
185 // Is the group already added?
186 if (!$this->isGroupSet($groupName)) {
187 // Throw the exception here
188 throw new NoListGroupException(array($this, $groupName), self::EXCEPTION_GROUP_NOT_FOUND);
192 $hash = $this->generateHash($groupName, $groupName, $entry);
193 //* DEBUG: */ $this->debugOutput(__METHOD__ . ': groupName=' . $groupName . ', entry=' . $entry . ', hash=' . $hash);
195 // Add the hash to the index
196 $this->listIndex[] = $hash;
197 //* DEBUG: */ print $groupName.'/'.count($this->listIndex)."\n";
199 // Now add the entry to the list
200 $this->listEntries[$hash] = $entry;
201 //* DEBUG: */ print $groupName.'/'.count($this->listEntries)."\n";
202 //* DEBUG: */ $this->debugOutput(__METHOD__.'('.$this->__toString().'): '.$groupName . ' - FINISHED');
206 * Removes given entry from the list group
208 * @param $groupName Group where we should remove the entry from
209 * @param $entry The entry we should remove
211 * @throws NoListGroupException If the given group is not found
213 public function removeEntry ($groupName, $entry) {
214 //* DEBUG: */ $this->debugOutput(__METHOD__.'('.$this->__toString().'): '.$groupName . ' - START');
215 // Is the group already added?
216 if (!$this->isGroupSet($groupName)) {
217 // Throw the exception here
218 throw new NoListGroupException(array($this, $groupName), self::EXCEPTION_GROUP_NOT_FOUND);
222 $hash = $this->generateHash($groupName, $groupName, $entry);
223 //* DEBUG: */ $this->debugOutput(__METHOD__ . ': groupName=' . $groupName . ', entry=' . $entry . ', hash=' . $hash);
225 // Remove it from the list ...
226 unset($this->listEntries[$hash]);
228 // ... and hash list as well
229 unset($this->listIndex[array_search($hash, $this->listIndex)]);
230 //* DEBUG: */ $this->debugOutput(__METHOD__.'('.$this->__toString().'): '.$groupName . ' - FINISHED');
234 * Generates a hash from given group, sub group and entry
236 * @param $groupName Group to add instance to
237 * @param $subGroup Sub group to add instance to
238 * @param $entry An entry of any type
239 * @return $hash The generated
241 private function generateHash ($groupName, $subGroup, $entry) {
242 // Created entry, 'null' is default
245 // Determine type of entry
246 if (is_null($entry)) {
248 } elseif ($entry instanceof FrameworkInterface) {
249 // Own instance detected
250 $entry2 = $entry->hashCode();
251 } elseif ((is_int($entry)) || (is_float($entry)) || (is_resource($entry))) {
252 // Integer/float/resource detected
253 $entry2 = gettype($entry) . ':' . $entry;
254 } elseif (is_string($entry)) {
256 $entry2 = crc32($entry) . ':' . strlen($entry);
257 } elseif ((is_array($entry)) && (isset($entry['id']))) {
258 // Supported array found
259 $entry2 = crc32($entry['id']) . ':' . count($entry);
261 // Unsupported type detected
262 $this->debugOutut(__METHOD__ . ': entry type ' . gettype($entry) . ' is unsupported.');
264 // @TODO Extend this somehow?
265 $entry2 = gettype($entry);
268 // Construct string which we shall hash
269 $hashString = $groupName . ':' . $subGroup . ':' . $entry2;
271 // Hash it with fastest hasher
272 $hash = crc32($hashString);
279 * Counts all entries in this list
281 * @return $count All entries in this list
283 public final function count () {
284 return count($this->listIndex);
288 * Checks wether the given hash is valid
290 * @param $hash The hash we should validate
291 * @return $isValid Wether the given hash is valid
293 public final function isHashValid ($hash) {
295 $isValid = ((in_array($hash, $this->listIndex)) && (isset($this->listEntries[$hash])));
302 * Getter for hash from given hash index
304 * @param $hashIndex Index holding the hash
305 * @return $hash The hash
307 public final function getHash ($hashIndex) {
309 $hash = $this->listIndex[$hashIndex];
316 * Gets an entry from given hash index
318 * @param $hashIndex The hash index to resolve the mapped entry
319 * @return $entry Solved entry from list
320 * @throws InvalidListHashException If the solved hash index is invalid
322 public function getEntry ($hashIndex) {
323 // Get the hash value
324 $hash = $this->getHash($hashIndex);
326 // Is the hash valid?
327 if (!$this->isHashValid($hash)) {
328 // Throw an exception here
329 throw new InvalidListHashException(array($this, $hash, $hashIndex), self::EXCEPTION_INVALID_HASH);
332 // Now copy the entry
333 $entry = $this->listEntries[$hash];
340 * Gets a full array from given group name
342 * @param $groupName The group name to get a list for
343 * @return $entries The array with all entries
344 * @throws NoListGroupException If the specified group is invalid
346 public function getArrayFromGroup ($groupName) {
347 // Is the group valid?
348 if (!$this->isGroupSet($groupName)) {
349 // Throw the exception here
350 throw new NoListGroupException(array($this, $groupName), self::EXCEPTION_GROUP_NOT_FOUND);
353 // Init the entries' array
357 $iteratorInstance = $this->listGroups[$groupName]->getIterator();
359 // Go through all entries
360 while ($iteratorInstance->valid()) {
362 $entryIndex = $iteratorInstance->key();
364 // ... and the final entry which is the stored instance
365 $entry = $this->getEntry($entryIndex);
367 // Add it to the list
368 $entries[$iteratorInstance->current()] = $entry;
371 $iteratorInstance->next();
379 * Updates the given entry by hash with given array
381 * @param $hash Hash for this entry
382 * @param $entryArray Array with entry we should update
384 * @throws InvalidListHashException If the solved hash index is invalid
386 public function updateCurrentEntryByHash ($hash, array $entryArray) {
387 // Is the hash valid?
388 if (!$this->isHashValid($hash)) {
389 // Throw an exception here, hashIndex is unknown at this point
390 throw new InvalidListHashException(array($this, $hash, -999), self::EXCEPTION_INVALID_HASH);
394 $this->listEntries[$hash] = $entryArray;