* @version 0.0.0 * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2023 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 . */ abstract class BaseStacker extends BaseFrameworkSystem { /** * Array "caches" configuration entries for saving "expensive" method * invocations */ private $cachedMaxStackSizes = []; /** * Protected constructor * * @param $className Name of the class * @return void */ protected function __construct (string $className) { // Call parent constructor parent::__construct($className); } /** * Initializes given stacker * * @param $stackerName Name of the stack * @param $forceReInit Force re-initialization * @return void * @throws InvalidArgumentException If a parameter is invalid * @throws BadMethodCallException If the stack is already initialized */ public function initStack (string $stackerName, bool $forceReInit = false) { // Validate parameter //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s,forceReInit=%d - CALLED!', $stackerName, intval($forceReInit))); if (empty($stackerName)) { // No empty stack name throw new InvalidArgumentException('Parameter "stackerName" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT); } elseif (($forceReInit === false) && ($this->isStackInitialized($stackerName))) { // Then throw the exception throw new BadMethodCallException(array($this, $stackerName, $forceReInit), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL); } // Initialize the given stack and "cache" configuration entry $this->initGenericArrayKey('stacks', $stackerName, 'entries', $forceReInit); $this->cachedMaxStackSizes[$stackerName] = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry(sprintf('stacker_%s_max_size', $stackerName)); // Trace message //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-STACKER: EXIT!'); } /** * Initializes all stacks * * @return void * @throws InvalidArgumentException If a parameter is invalid */ public function initStacks (array $stacks, bool $forceReInit = false) { // Validate parameter //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stacks()=%d,forceReInit=%d - CALLED!', count($stacks), intval($forceReInit))); if (count($stacks) == 0) { // No empty stack name throw new InvalidArgumentException('Array "stacks" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT); } // "Walk" through all (more will be added as needed foreach ($stacks as $stackerName) { // Init this stack //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: Invoking this->initStack(%s,%d) ...', $stackerName, intval($forceReInit))); $this->initStack($stackerName, $forceReInit); } // Trace message //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-STACKER: EXIT!'); } /** * Checks whether the given stack is initialized (set in array $stackers) * * @param $stackerName Name of the stack * @return $isInitialized Whether the stack is initialized * @throws InvalidArgumentException If a parameter is invalid */ public function isStackInitialized (string $stackerName) { // Validate parameter //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s - CALLED!', $stackerName)); if (empty($stackerName)) { // No empty stack name throw new InvalidArgumentException('Parameter "stackerName" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT); } // Is is there? $isInitialized = ($this->isValidGenericArrayKey('stacks', $stackerName, 'entries')); // Return result //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: isInitialized=%d - EXIT!', intval($isInitialized))); return $isInitialized; } /** * Checks whether the given stack is full * * @param $stackerName Name of the stack * @return $isFull Whether the stack is full * @throws InvalidArgumentException If a parameter is invalid * @throws BadMethodCallException If given stack is missing */ protected function isStackFull (string $stackerName) { // Validate parameter //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s - CALLED!', $stackerName)); if (empty($stackerName)) { // No empty stack name throw new InvalidArgumentException('Parameter "stackerName" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT); } elseif (!$this->isStackInitialized($stackerName)) { // Throw an exception throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL); } // So, is the stack full? $isFull = (($this->getStackCount($stackerName)) == $this->cachedMaxStackSizes[$stackerName]); // Return result //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: isFull=%d - EXIT!', intval($isFull))); return $isFull; } /** * Checks whether the given stack is empty * * @param $stackerName Name of the stack * @return $isEmpty Whether the stack is empty * @throws InvalidArgumentException If a parameter is invalid * @throws BadMethodCallException If given stack is missing */ public function isStackEmpty (string $stackerName) { // Validate parameter //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s - CALLED!', $stackerName)); if (empty($stackerName)) { // No empty stack name throw new InvalidArgumentException('Parameter "stackerName" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT); } elseif (!$this->isStackInitialized($stackerName)) { // Throw an exception throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL); } // So, is the stack empty? $isEmpty = (($this->getStackCount($stackerName)) == 0); // Return result //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: isEmpty=%d - EXIT!', intval($isEmpty))); return $isEmpty; } /** * Getter for size of given stack (array count) * * @param $stackerName Name of the stack * @return $count Size of stack (array count) * @throws InvalidArgumentException If a parameter is invalid * @throws BadMethodCallException If given stack is missing */ public function getStackCount (string $stackerName) { // Validate parameter //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s - CALLED!', $stackerName)); if (empty($stackerName)) { // No empty stack name throw new InvalidArgumentException('Parameter "stackerName" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT); } elseif (!$this->isStackInitialized($stackerName)) { // Throw an exception throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL); } // Now, count the array of entries $count = $this->countGenericArrayElements('stacks', $stackerName, 'entries'); // Return result //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: count=%d - EXIT!', $count)); return $count; } /** * Adds a value to given stack * * @param $stackerName Name of the stack * @param $value Value to add to this stacker * @return void * @throws InvalidArgumentException If a parameter is invalid * @throws BadMethodCallException If given stack is missing * @throws FullStackerException Thrown if the stack is full */ protected function addValueToStack (string $stackerName, $value) { // Validate parameter //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s,value[]=%s - CALLED!', $stackerName, gettype($value))); if (empty($stackerName)) { // No empty stack name throw new InvalidArgumentException('Parameter "stackerName" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT); } elseif (!$this->isStackInitialized($stackerName)) { // Throw an exception throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL); } elseif ($this->isStackFull($stackerName)) { // Stacker is full throw new BadMethodCallException(sprintf('stackerName=%s is full but method called.', $stackerName), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL); } // Now add the value to the stack $this->pushValueToGenericArrayKey('stacks', $stackerName, 'entries', $value); // Trace message //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-STACKER: EXIT!'); } /** * Get last value from named stacker * * @param $stackerName Name of the stack * @return $value Value of last added value * @throws InvalidArgumentException If a parameter is invalid * @throws BadMethodCallException If the named stacker was not found * @throws BadMethodCallException If the named stacker is empty */ protected function getLastValue (string $stackerName) { // Validate parameter //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s - CALLED!', $stackerName)); if (empty($stackerName)) { // No empty stack name throw new InvalidArgumentException('Parameter "stackerName" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT); } elseif (!$this->isStackInitialized($stackerName)) { // Throw an exception throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL); } elseif ($this->isStackEmpty($stackerName)) { // Throw an exception throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL); } // Calculate last index $lastIndex = $this->getStackCount($stackerName) - 1; // Now get the last value $value = $this->getGenericArrayElement('stacks', $stackerName, 'entries', $lastIndex); // Return it //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: value[]=%s - EXIT!', gettype($value))); return $value; } /** * Get first value from named stacker * * @param $stackerName Name of the stack * @return $value Value of last added value * @throws InvalidArgumentException If a parameter is invalid * @throws BadMethodCallException If the named stacker was not found * @throws BadMethodCallException If the named stacker is empty */ protected function getFirstValue (string $stackerName) { // Validate parameter //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s - CALLED!', $stackerName)); if (empty($stackerName)) { // No empty stack name throw new InvalidArgumentException('Parameter "stackerName" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT); } elseif (!$this->isStackInitialized($stackerName)) { // Throw an exception throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL); } elseif ($this->isStackEmpty($stackerName)) { // Throw an exception throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL); } // Now get the first value $value = $this->getGenericArrayElement('stacks', $stackerName, 'entries', '0'); // Return it //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: value[]=%s - EXIT!', gettype($value))); return $value; } /** * "Pops" last entry from stack * * @param $stackerName Name of the stack * @return $value Value "poped" from array * @throws InvalidArgumentException If a parameter is invalid * @throws BadMethodCallException If the named stacker was not found * @throws BadMethodCallException If the named stacker is empty */ protected function popLast (string $stackerName) { // Validate parameter //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s - CALLED!', $stackerName)); if (empty($stackerName)) { // No empty stack name throw new InvalidArgumentException('Parameter "stackerName" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT); } elseif (!$this->isStackInitialized($stackerName)) { // Throw an exception throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL); } elseif ($this->isStackEmpty($stackerName)) { // Throw an exception throw new BadMethodCallException(sprintf('stackerName=%s is empty but method called.', $stackerName), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL); } // Now, remove the last entry, we don't care about the return value here, see elseif() block above $value = $this->popGenericArrayElement('stacks', $stackerName, 'entries'); // Return it //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: value[]=%s - EXIT!', gettype($value))); return $value; } /** * "Pops" first entry from stack * * @param $stackerName Name of the stack * @return $value Value "shifted" from array * @throws InvalidArgumentException If a parameter is invalid * @throws BadMethodCallException If the named stacker was not found * @throws BadMethodCallException If the named stacker is empty */ protected function popFirst (string $stackerName) { // Validate parameter //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s - CALLED!', $stackerName)); if (empty($stackerName)) { // No empty stack name throw new InvalidArgumentException('Parameter "stackerName" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT); } elseif (!$this->isStackInitialized($stackerName)) { // Throw an exception throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL); } elseif ($this->isStackEmpty($stackerName)) { // Throw an exception throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL); } // Now, remove the last entry, we don't care about the return value here, see elseif() block above $value = $this->shiftGenericArrayElement('stacks', $stackerName, 'entries'); // Return it //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: value[]=%s - EXIT!', gettype($value))); return $value; } }