* @version 0.0.0 * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2017 Core Developer Team * @license GNU GPL 3.0 or any newer version * @link http://www.shipsimu.org * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ class BaseList extends BaseFrameworkSystem implements IteratorAggregate, Countable { // Exception constants const EXCEPTION_GROUP_ALREADY_ADDED = 0xf20; const EXCEPTION_GROUP_NOT_FOUND = 0xf21; const EXCEPTION_INVALID_HASH = 0xf22; /** * Call-back instance */ private $callbackInstance = NULL; /** * List groups array */ private $listGroups = array(); /** * List entries array */ private $listEntries = array(); /** * List index array */ private $listIndex = array(); /** * Protected constructor * * @param $className Name of the class * @return void */ protected function __construct ($className) { // Call parent constructor parent::__construct($className); } /** * Setter for call-back instance * * @param $callbackInstance An instance of a FrameworkInterface class * @return void */ public final function setCallbackInstance (FrameworkInterface $callbackInstance) { $this->callbackInstance = $callbackInstance; } /** * Getter for iterator instance from this list * * @return $iteratorInstance An instance of a Iterator class */ public function getIterator () { // Get iterator from here $iteratorInstance = $this->getIteratorInstance(); // Is the instance set? if (is_null($iteratorInstance)) { // Prepare a default iterator $iteratorInstance = ObjectFactory::createObjectByConfiguredName('default_iterator_class', array($this)); // Set it here $this->setIteratorInstance($iteratorInstance); } // END - if // And return it return $iteratorInstance; } /** * Checks whether the given group is set * * @param $groupName Group to check if found in list * @return $isset Whether the group is valid */ public function isGroupSet ($groupName) { //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName); return isset($this->listGroups[$groupName]); } /** * Adds the given group or if already added issues a ListGroupAlreadyAddedException * * @param $groupName Group to add * @return void * @throws ListGroupAlreadyAddedException If the given group is already added */ public function addGroup ($groupName) { //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ' - CALLED!'); // Is the group already added? if ($this->isGroupSet($groupName)) { // Throw the exception here throw new ListGroupAlreadyAddedException(array($this, $groupName), self::EXCEPTION_GROUP_ALREADY_ADDED); } // END - if // Add the group which is a simple array $this->listGroups[$groupName] = ObjectFactory::createObjectByConfiguredName('list_group_class'); //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ' - EXIT!'); } /** * Adds the given instance to list group and sub group * * @param $groupName Group to add instance to * @param $subGroup Sub group to add instance to * @param $visitableInstance An instance of Visitable * @return void * @throws NoListGroupException If the given group is not found */ public function addInstance ($groupName, $subGroup, Visitable $visitableInstance) { // Debug message //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',subGroup=' . $subGroup . ',visitableInstance=' . $visitableInstance->__toString() . ' - CALLED!'); // Is the group there? if (!$this->isGroupSet($groupName)) { // Throw the exception here throw new NoListGroupException(array($this, $groupName), self::EXCEPTION_GROUP_NOT_FOUND); } // END - if // Is the sub group there? if (!$this->listGroups[$groupName]->isGroupSet($subGroup)) { // Automatically add it $this->listGroups[$groupName]->addGroup($subGroup); } // END - if // Generate the hash $hash = $this->generateHash($groupName, $subGroup, $visitableInstance); // Debug message //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',this->listGroups[' . $groupName . ']=' . $this->listGroups[$groupName]->__toString()); // Add the hash to the index array_push($this->listIndex, $hash); // Add the instance itself to the list $this->listEntries[$hash] = $visitableInstance; // Debug message //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',subGroup=' . $subGroup . ' - EXIT!'); } /** * Gets an array from given list * * @param $list The requested list * @return $array The requested array * @throws NoListGroupException If the given group is not found */ public final function getArrayFromList ($list) { // Debug message //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',list[' . gettype($list) . ']=' . $list . ' - CALLED!'); // Is the group there? if ((!is_null($list)) && (!$this->isGroupSet($list))) { // Throw the exception here throw new NoListGroupException(array($this, $list), self::EXCEPTION_GROUP_NOT_FOUND); } // END - if // Init array $array = array(); // Is there another list? if (!is_null($list)) { // Then get it as well $array = $this->listGroups[$list]->getArrayFromList(NULL); } // END - if // Walk through all entries foreach ($this->listIndex as $hash) { // Debug message //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: hash=' . $hash); // Is the list entry set? if ($this->isHashValid($hash)) { // Add it array_push($array, $this->listEntries[$hash]); //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: hash=' . $hash . ',array(' . count($array) . ')=' . print_r($array, true) . ' - ADDED!'); } // END - if } // END - foreach // Debug message //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',list[' . gettype($list) . ']=' . $list . ',array()=' . count($array) . ' - EXIT!'); // Return it return $array; } /** * Adds the given entry to list group * * @param $groupName Group to add instance to * @param $entry An entry of any type * @return void * @throws NoListGroupException If the given group is not found */ public function addEntry ($groupName, $entry) { // Debug message //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ' - CALLED!'); // Is the group already added? if (!$this->isGroupSet($groupName)) { // Throw the exception here throw new NoListGroupException(array($this, $groupName), self::EXCEPTION_GROUP_NOT_FOUND); } // END - if // Generate hash $hash = $this->generateHash($groupName, $groupName, $entry); // Debug message //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',entry=' . print_r($entry, true) . ', hash=' . $hash); // Add the hash to the index array_push($this->listIndex, $hash); // Debug message //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',listEntries()=' . count($this->listEntries)); // Now add the entry to the list $this->listEntries[$hash] = $entry; // Debug message //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',listEntries()=' . count($this->listEntries) . ' - EXIT!'); } /** * Removes given entry from the list group * * @param $groupName Group where we should remove the entry from * @param $entry The entry we should remove * @return void * @throws NoListGroupException If the given group is not found */ public function removeEntry ($groupName, $entry) { // Debug message //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ' - CALLED!'); // Is the group already added? if (!$this->isGroupSet($groupName)) { // Throw the exception here throw new NoListGroupException(array($this, $groupName), self::EXCEPTION_GROUP_NOT_FOUND); } // END - if // Generate hash $hash = $this->generateHash($groupName, $groupName, $entry); // Debug message //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ',entry=' . $entry . ', hash=' . $hash); // Remove it from the list ... unset($this->listEntries[$hash]); // ... and hash list as well unset($this->listIndex[array_search($hash, $this->listIndex)]); // Debug message //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('[' . __METHOD__ . ':' . __LINE__ . ']: this=' . $this->__toString() . ',groupName=' . $groupName . ' - EXIT!'); } /** * Generates a hash from given group, sub group and entry * * @param $groupName Group to add instance to * @param $subGroup Sub group to add instance to * @param $entry An entry of any type * @return $hash The generated */ private function generateHash ($groupName, $subGroup, $entry) { // Created entry, 'null' is default $entry2 = 'null'; // Determine type of entry if (is_null($entry)) { // Ignore this } elseif ($entry instanceof FrameworkInterface) { // Own instance detected $entry2 = $entry->hashCode(); } elseif ((is_int($entry)) || (is_float($entry)) || (is_resource($entry))) { // Integer/float/resource detected $entry2 = gettype($entry) . ':' . $entry; } elseif (is_string($entry)) { // String found $entry2 = crc32($entry) . ':' . strlen($entry); } elseif ((is_array($entry)) && (isset($entry['id']))) { // Supported array found $entry2 = crc32($entry['id']) . ':' . count($entry); } elseif (($this->callbackInstance instanceof FrameworkInterface) && (method_exists($this->callbackInstance, 'generateListHashFromEntry'))) { // Call it instead $entry2 = $this->callbackInstance->generateListHashFromEntry($entry); } else { // Unsupported type detected self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-LIST[' . __METHOD__ . ':' . __LINE__ . ']: Entry type ' . gettype($entry) . ' is unsupported (this->callbackInstance=' . $this->callbackInstance . ').'); // At least take all keys from array $entry2 = gettype($entry) . ':' . implode(':', array_keys($entry)); } // Construct string which we shall hash $hashString = $groupName . ':' . $subGroup . ':' . $entry2; // Hash it with fastest hasher $hash = crc32($hashString); // And return it return $hash; } /** * Clears an array of groups, all are being checked for existence * * @param $groupNames An array with existing list groups * @return void */ protected function clearGroups (array $groupNames) { // Walk through all groups foreach ($groupNames as $groupName) { // Clear this group $this->clearGroup($groupName); } // END - foreach } /** * Clears a single group by resetting it to its initial state (empty array) * * @param $groupName Name of an existing group to clear * @return void */ protected function clearGroup ($groupName) { // Does this group exist? if (!$this->isGroupSet($groupName)) { // Throw the exception here throw new NoListGroupException(array($this, $groupName), self::EXCEPTION_GROUP_NOT_FOUND); } // END - if // Then clear this group list $this->listGroups[$groupName]->clearList(); // Clear this list $this->listIndex = array(); $this->listEntries = array(); } /** * Counts all entries in this list * * @return $count All entries in this list */ public final function count () { return count($this->listIndex); } /** * Checks whether the given hash is valid * * @param $hash The hash we should validate * @return $isValid Whether the given hash is valid */ public final function isHashValid ($hash) { // Check it $isValid = ((in_array($hash, $this->listIndex)) && (isset($this->listEntries[$hash]))); // Return the result return $isValid; } /** * Getter for hash from given hash index * * @param $hashIndex Index holding the hash * @return $hash The hash */ public final function getHash ($hashIndex) { // Get it ... $hash = $this->listIndex[$hashIndex]; // ... and return it return $hash; } /** * Gets an entry from given hash index * * @param $hashIndex The hash index to resolve the mapped entry * @return $entry Solved entry from list * @throws InvalidListHashException If the solved hash index is invalid */ public function getEntry ($hashIndex) { // Get the hash value $hash = $this->getHash($hashIndex); // Is the hash valid? if (!$this->isHashValid($hash)) { // Throw an exception here throw new InvalidListHashException(array($this, $hash, $hashIndex), self::EXCEPTION_INVALID_HASH); } // END - if // Now copy the entry $entry = $this->listEntries[$hash]; // Return it return $entry; } /** * Gets a full array from given group name * * @param $groupName The group name to get a list for * @return $entries The array with all entries * @throws NoListGroupException If the specified group is invalid */ public function getArrayFromProtocolInstance ($groupName) { // Is the group valid? if (!$this->isGroupSet($groupName)) { // Throw the exception here throw new NoListGroupException(array($this, $groupName), self::EXCEPTION_GROUP_NOT_FOUND); } // END - if // Init the entries' array $entries = array(); // Get an iterator $iteratorInstance = $this->listGroups[$groupName]->getIterator(); // Rewind the iterator for this round $iteratorInstance->rewind(); // Go through all entries while ($iteratorInstance->valid()) { // Get key $entryIndex = $iteratorInstance->key(); // ... and the final entry which is the stored instance $entry = $this->getEntry($entryIndex); // Debug message //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('LIST: Adding entry ' . $entry . ' ...'); // Add it to the list $entries[$iteratorInstance->current()] = $entry; // Skip to next one $iteratorInstance->next(); } // END - while // Return the list return $entries; } /** * Updates the given entry by hash with given array * * @param $hash Hash for this entry * @param $entryArray Array with entry we should update * @return void * @throws InvalidListHashException If the solved hash index is invalid */ public function updateCurrentEntryByHash ($hash, array $entryArray) { // Is the hash valid? if (!$this->isHashValid($hash)) { // Throw an exception here, hashIndex is unknown at this point throw new InvalidListHashException(array($this, $hash, -999), self::EXCEPTION_INVALID_HASH); } // END - if // Set the entry $this->listEntries[$hash] = $entryArray; } }