3 namespace Org\Mxchange\CoreFramework\Stack;
5 // Import framework stuff
6 use Org\Mxchange\CoreFramework\Bootstrap\FrameworkBootstrap;
7 use Org\Mxchange\CoreFramework\Generic\FrameworkInterface;
8 use Org\Mxchange\CoreFramework\Object\BaseFrameworkSystem;
11 use \BadMethodCallException;
12 use \InvalidArgumentException;
17 * @author Roland Haeder <webmaster@shipsimu.org>
19 * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2023 Core Developer Team
20 * @license GNU GPL 3.0 or any newer version
21 * @link http://www.shipsimu.org
23 * This program is free software: you can redistribute it and/or modify
24 * it under the terms of the GNU General Public License as published by
25 * the Free Software Foundation, either version 3 of the License, or
26 * (at your option) any later version.
28 * This program is distributed in the hope that it will be useful,
29 * but WITHOUT ANY WARRANTY; without even the implied warranty of
30 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
31 * GNU General Public License for more details.
33 * You should have received a copy of the GNU General Public License
34 * along with this program. If not, see <http://www.gnu.org/licenses/>.
36 abstract class BaseStacker extends BaseFrameworkSystem {
38 * Array "caches" configuration entries for saving "expensive" method
41 private $cachedMaxStackSizes = [];
44 * Protected constructor
46 * @param $className Name of the class
49 protected function __construct (string $className) {
50 // Call parent constructor
51 parent::__construct($className);
55 * Initializes given stacker
57 * @param $stackerName Name of the stack
58 * @param $forceReInit Force re-initialization
60 * @throws InvalidArgumentException If a parameter is invalid
61 * @throws BadMethodCallException If the stack is already initialized
63 public function initStack (string $stackerName, bool $forceReInit = false) {
65 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s,forceReInit=%d - CALLED!', $stackerName, intval($forceReInit)));
66 if (empty($stackerName)) {
67 // No empty stack name
68 throw new InvalidArgumentException('Parameter "stackerName" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
69 } elseif (($forceReInit === false) && ($this->isStackInitialized($stackerName))) {
70 // Then throw the exception
71 throw new BadMethodCallException(array($this, $stackerName, $forceReInit), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL);
74 // Initialize the given stack and "cache" configuration entry
75 $this->initGenericArrayKey('stacks', $stackerName, 'entries', $forceReInit);
76 $this->cachedMaxStackSizes[$stackerName] = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry(sprintf('stacker_%s_max_size', $stackerName));
79 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-STACKER: EXIT!');
83 * Initializes all stacks
86 * @throws InvalidArgumentException If a parameter is invalid
88 public function initStacks (array $stacks, bool $forceReInit = false) {
90 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stacks()=%d,forceReInit=%d - CALLED!', count($stacks), intval($forceReInit)));
91 if (count($stacks) == 0) {
92 // No empty stack name
93 throw new InvalidArgumentException('Array "stacks" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
96 // "Walk" through all (more will be added as needed
97 foreach ($stacks as $stackerName) {
99 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: Invoking this->initStack(%s,%d) ...', $stackerName, intval($forceReInit)));
100 $this->initStack($stackerName, $forceReInit);
104 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-STACKER: EXIT!');
108 * Checks whether the given stack is initialized (set in array $stackers)
110 * @param $stackerName Name of the stack
111 * @return $isInitialized Whether the stack is initialized
112 * @throws InvalidArgumentException If a parameter is invalid
114 public function isStackInitialized (string $stackerName) {
115 // Validate parameter
116 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s - CALLED!', $stackerName));
117 if (empty($stackerName)) {
118 // No empty stack name
119 throw new InvalidArgumentException('Parameter "stackerName" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
123 $isInitialized = ($this->isValidGenericArrayKey('stacks', $stackerName, 'entries'));
126 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: isInitialized=%d - EXIT!', intval($isInitialized)));
127 return $isInitialized;
131 * Checks whether the given stack is full
133 * @param $stackerName Name of the stack
134 * @return $isFull Whether the stack is full
135 * @throws InvalidArgumentException If a parameter is invalid
136 * @throws BadMethodCallException If given stack is missing
138 protected function isStackFull (string $stackerName) {
139 // Validate parameter
140 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s - CALLED!', $stackerName));
141 if (empty($stackerName)) {
142 // No empty stack name
143 throw new InvalidArgumentException('Parameter "stackerName" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
144 } elseif (!$this->isStackInitialized($stackerName)) {
145 // Throw an exception
146 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL);
149 // So, is the stack full?
150 $isFull = (($this->getStackCount($stackerName)) == $this->cachedMaxStackSizes[$stackerName]);
153 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: isFull=%d - EXIT!', intval($isFull)));
158 * Checks whether the given stack is empty
160 * @param $stackerName Name of the stack
161 * @return $isEmpty Whether the stack is empty
162 * @throws InvalidArgumentException If a parameter is invalid
163 * @throws BadMethodCallException If given stack is missing
165 public function isStackEmpty (string $stackerName) {
166 // Validate parameter
167 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s - CALLED!', $stackerName));
168 if (empty($stackerName)) {
169 // No empty stack name
170 throw new InvalidArgumentException('Parameter "stackerName" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
171 } elseif (!$this->isStackInitialized($stackerName)) {
172 // Throw an exception
173 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL);
176 // So, is the stack empty?
177 $isEmpty = (($this->getStackCount($stackerName)) == 0);
180 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: isEmpty=%d - EXIT!', intval($isEmpty)));
185 * Getter for size of given stack (array count)
187 * @param $stackerName Name of the stack
188 * @return $count Size of stack (array count)
189 * @throws InvalidArgumentException If a parameter is invalid
190 * @throws BadMethodCallException If given stack is missing
192 public function getStackCount (string $stackerName) {
193 // Validate parameter
194 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s - CALLED!', $stackerName));
195 if (empty($stackerName)) {
196 // No empty stack name
197 throw new InvalidArgumentException('Parameter "stackerName" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
198 } elseif (!$this->isStackInitialized($stackerName)) {
199 // Throw an exception
200 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL);
203 // Now, count the array of entries
204 $count = $this->countGenericArrayElements('stacks', $stackerName, 'entries');
207 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: count=%d - EXIT!', $count));
212 * Adds a value to given stack
214 * @param $stackerName Name of the stack
215 * @param $value Value to add to this stacker
217 * @throws InvalidArgumentException If a parameter is invalid
218 * @throws BadMethodCallException If given stack is missing
219 * @throws FullStackerException Thrown if the stack is full
221 protected function addValueToStack (string $stackerName, $value) {
222 // Validate parameter
223 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s,value[]=%s - CALLED!', $stackerName, gettype($value)));
224 if (empty($stackerName)) {
225 // No empty stack name
226 throw new InvalidArgumentException('Parameter "stackerName" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
227 } elseif (!$this->isStackInitialized($stackerName)) {
228 // Throw an exception
229 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL);
230 } elseif ($this->isStackFull($stackerName)) {
232 throw new BadMethodCallException(sprintf('stackerName=%s is full but method called.', $stackerName), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL);
235 // Now add the value to the stack
236 $this->pushValueToGenericArrayKey('stacks', $stackerName, 'entries', $value);
239 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-STACKER: EXIT!');
243 * Get last value from named stacker
245 * @param $stackerName Name of the stack
246 * @return $value Value of last added value
247 * @throws InvalidArgumentException If a parameter is invalid
248 * @throws BadMethodCallException If the named stacker was not found
249 * @throws BadMethodCallException If the named stacker is empty
251 protected function getLastValue (string $stackerName) {
252 // Validate parameter
253 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s - CALLED!', $stackerName));
254 if (empty($stackerName)) {
255 // No empty stack name
256 throw new InvalidArgumentException('Parameter "stackerName" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
257 } elseif (!$this->isStackInitialized($stackerName)) {
258 // Throw an exception
259 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL);
260 } elseif ($this->isStackEmpty($stackerName)) {
261 // Throw an exception
262 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL);
265 // Calculate last index
266 $lastIndex = $this->getStackCount($stackerName) - 1;
268 // Now get the last value
269 $value = $this->getGenericArrayElement('stacks', $stackerName, 'entries', $lastIndex);
272 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: value[]=%s - EXIT!', gettype($value)));
277 * Get first value from named stacker
279 * @param $stackerName Name of the stack
280 * @return $value Value of last added value
281 * @throws InvalidArgumentException If a parameter is invalid
282 * @throws BadMethodCallException If the named stacker was not found
283 * @throws BadMethodCallException If the named stacker is empty
285 protected function getFirstValue (string $stackerName) {
286 // Validate parameter
287 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s - CALLED!', $stackerName));
288 if (empty($stackerName)) {
289 // No empty stack name
290 throw new InvalidArgumentException('Parameter "stackerName" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
291 } elseif (!$this->isStackInitialized($stackerName)) {
292 // Throw an exception
293 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL);
294 } elseif ($this->isStackEmpty($stackerName)) {
295 // Throw an exception
296 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL);
299 // Now get the first value
300 $value = $this->getGenericArrayElement('stacks', $stackerName, 'entries', '0');
303 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: value[]=%s - EXIT!', gettype($value)));
308 * "Pops" last entry from stack
310 * @param $stackerName Name of the stack
311 * @return $value Value "poped" from array
312 * @throws InvalidArgumentException If a parameter is invalid
313 * @throws BadMethodCallException If the named stacker was not found
314 * @throws BadMethodCallException If the named stacker is empty
316 protected function popLast (string $stackerName) {
317 // Validate parameter
318 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s - CALLED!', $stackerName));
319 if (empty($stackerName)) {
320 // No empty stack name
321 throw new InvalidArgumentException('Parameter "stackerName" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
322 } elseif (!$this->isStackInitialized($stackerName)) {
323 // Throw an exception
324 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL);
325 } elseif ($this->isStackEmpty($stackerName)) {
326 // Throw an exception
327 throw new BadMethodCallException(sprintf('stackerName=%s is empty but method called.', $stackerName), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL);
330 // Now, remove the last entry, we don't care about the return value here, see elseif() block above
331 $value = $this->popGenericArrayElement('stacks', $stackerName, 'entries');
334 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: value[]=%s - EXIT!', gettype($value)));
339 * "Pops" first entry from stack
341 * @param $stackerName Name of the stack
342 * @return $value Value "shifted" from array
343 * @throws InvalidArgumentException If a parameter is invalid
344 * @throws BadMethodCallException If the named stacker was not found
345 * @throws BadMethodCallException If the named stacker is empty
347 protected function popFirst (string $stackerName) {
348 // Validate parameter
349 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s - CALLED!', $stackerName));
350 if (empty($stackerName)) {
351 // No empty stack name
352 throw new InvalidArgumentException('Parameter "stackerName" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
353 } elseif (!$this->isStackInitialized($stackerName)) {
354 // Throw an exception
355 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL);
356 } elseif ($this->isStackEmpty($stackerName)) {
357 // Throw an exception
358 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL);
361 // Now, remove the last entry, we don't care about the return value here, see elseif() block above
362 $value = $this->shiftGenericArrayElement('stacks', $stackerName, 'entries');
365 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: value[]=%s - EXIT!', gettype($value)));