3 namespace Org\Mxchange\CoreFramework\Stack;
5 // Import framework stuff
6 use Org\Mxchange\CoreFramework\Bootstrap\FrameworkBootstrap;
7 use Org\Mxchange\CoreFramework\Object\BaseFrameworkSystem;
10 use \BadMethodCallException;
11 use \InvalidArgumentException;
16 * @author Roland Haeder <webmaster@shipsimu.org>
18 * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2022 Core Developer Team
19 * @license GNU GPL 3.0 or any newer version
20 * @link http://www.shipsimu.org
22 * This program is free software: you can redistribute it and/or modify
23 * it under the terms of the GNU General Public License as published by
24 * the Free Software Foundation, either version 3 of the License, or
25 * (at your option) any later version.
27 * This program is distributed in the hope that it will be useful,
28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 * GNU General Public License for more details.
32 * You should have received a copy of the GNU General Public License
33 * along with this program. If not, see <http://www.gnu.org/licenses/>.
35 abstract class BaseStacker extends BaseFrameworkSystem {
37 const EXCEPTION_STACKER_ALREADY_INITIALIZED = 0x050;
38 const EXCEPTION_STACKER_IS_FULL = 0x051;
39 const EXCEPTION_NO_STACKER_FOUND = 0x052;
40 const EXCEPTION_STACKER_IS_EMPTY = 0x053;
43 * Array "caches" configuration entries for saving "expensive" method
46 private $cachedMaxStackSizes = [];
49 * Protected constructor
51 * @param $className Name of the class
54 protected function __construct (string $className) {
55 // Call parent constructor
56 parent::__construct($className);
60 * Initializes given stacker
62 * @param $stackerName Name of the stack
63 * @param $forceReInit Force re-initialization
65 * @throws InvalidArgumentException If a parameter is invalid
66 * @throws BadMethodCallException If the stack is already initialized
68 public function initStack (string $stackerName, bool $forceReInit = false) {
70 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s,forceReInit=%d - CALLED!', $stackerName, intval($forceReInit)));
71 if (empty($stackerName)) {
72 // No empty stack name
73 throw new InvalidArgumentException('Parameter "stackerName" is empty');
74 } elseif (($forceReInit === false) && ($this->isStackInitialized($stackerName))) {
75 // Then throw the exception
76 throw new BadMethodCallException(array($this, $stackerName, $forceReInit), self::EXCEPTION_STACKER_ALREADY_INITIALIZED);
79 // Initialize the given stack and "cache" configuration entry
80 $this->initGenericArrayKey('stacks', $stackerName, 'entries', $forceReInit);
81 $this->cachedMaxStackSizes[$stackerName] = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry(sprintf('stacker_%s_max_size', $stackerName));
84 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-STACKER: EXIT!');
88 * Initializes all stacks
91 * @throws InvalidArgumentException If a parameter is invalid
93 public function initStacks (array $stacks, bool $forceReInit = false) {
95 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stacks()=%d,forceReInit=%d - CALLED!', count($stacks), intval($forceReInit)));
96 if (count($stacks) == 0) {
97 // No empty stack name
98 throw new InvalidArgumentException('Array "stacks" is empty');
101 // "Walk" through all (more will be added as needed
102 foreach ($stacks as $stackerName) {
104 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: Calling this->initStack(%s,%d) ...', $stackerName, intval($forceReInit)));
105 $this->initStack($stackerName, $forceReInit);
109 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-STACKER: EXIT!');
113 * Checks whether the given stack is initialized (set in array $stackers)
115 * @param $stackerName Name of the stack
116 * @return $isInitialized Whether the stack is initialized
117 * @throws InvalidArgumentException If a parameter is invalid
119 public function isStackInitialized (string $stackerName) {
120 // Validate parameter
121 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s - CALLED!', $stackerName));
122 if (empty($stackerName)) {
123 // No empty stack name
124 throw new InvalidArgumentException('Parameter "stackerName" is empty');
128 $isInitialized = ($this->isValidGenericArrayKey('stacks', $stackerName, 'entries'));
131 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: isInitialized=%d - EXIT!', intval($isInitialized)));
132 return $isInitialized;
136 * Checks whether the given stack is full
138 * @param $stackerName Name of the stack
139 * @return $isFull Whether the stack is full
140 * @throws InvalidArgumentException If a parameter is invalid
141 * @throws BadMethodCallException If given stack is missing
143 protected function isStackFull (string $stackerName) {
144 // Validate parameter
145 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s - CALLED!', $stackerName));
146 if (empty($stackerName)) {
147 // No empty stack name
148 throw new InvalidArgumentException('Parameter "stackerName" is empty');
149 } elseif (!$this->isStackInitialized($stackerName)) {
150 // Throw an exception
151 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), self::EXCEPTION_NO_STACKER_FOUND);
154 // So, is the stack full?
155 $isFull = (($this->getStackCount($stackerName)) == $this->cachedMaxStackSizes[$stackerName]);
158 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: isFull=%d - EXIT!', intval($isFull)));
163 * Checks whether the given stack is empty
165 * @param $stackerName Name of the stack
166 * @return $isEmpty Whether the stack is empty
167 * @throws InvalidArgumentException If a parameter is invalid
168 * @throws BadMethodCallException If given stack is missing
170 public function isStackEmpty (string $stackerName) {
171 // Validate parameter
172 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s - CALLED!', $stackerName));
173 if (empty($stackerName)) {
174 // No empty stack name
175 throw new InvalidArgumentException('Parameter "stackerName" is empty');
176 } elseif (!$this->isStackInitialized($stackerName)) {
177 // Throw an exception
178 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), self::EXCEPTION_NO_STACKER_FOUND);
181 // So, is the stack empty?
182 $isEmpty = (($this->getStackCount($stackerName)) == 0);
185 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: isEmpty=%d - EXIT!', intval($isEmpty)));
190 * Getter for size of given stack (array count)
192 * @param $stackerName Name of the stack
193 * @return $count Size of stack (array count)
194 * @throws InvalidArgumentException If a parameter is invalid
195 * @throws BadMethodCallException If given stack is missing
197 public function getStackCount (string $stackerName) {
198 // Validate parameter
199 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s - CALLED!', $stackerName));
200 if (empty($stackerName)) {
201 // No empty stack name
202 throw new InvalidArgumentException('Parameter "stackerName" is empty');
203 } elseif (!$this->isStackInitialized($stackerName)) {
204 // Throw an exception
205 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), self::EXCEPTION_NO_STACKER_FOUND);
208 // Now, count the array of entries
209 $count = $this->countGenericArrayElements('stacks', $stackerName, 'entries');
212 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: count=%d - EXIT!', $count));
217 * Adds a value to given stack
219 * @param $stackerName Name of the stack
220 * @param $value Value to add to this stacker
222 * @throws InvalidArgumentException If a parameter is invalid
223 * @throws BadMethodCallException If given stack is missing
224 * @throws FullStackerException Thrown if the stack is full
226 protected function addValueToStack (string $stackerName, $value) {
227 // Validate parameter
228 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s,value[]=%s - CALLED!', $stackerName, gettype($value)));
229 if (empty($stackerName)) {
230 // No empty stack name
231 throw new InvalidArgumentException('Parameter "stackerName" is empty');
232 } elseif (!$this->isStackInitialized($stackerName)) {
233 // Throw an exception
234 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), self::EXCEPTION_NO_STACKER_FOUND);
235 } elseif ($this->isStackFull($stackerName)) {
237 throw new FullStackerException([$this, $stackerName, $value], self::EXCEPTION_STACKER_IS_FULL);
240 // Now add the value to the stack
241 $this->pushValueToGenericArrayKey('stacks', $stackerName, 'entries', $value);
244 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-STACKER: EXIT!');
248 * Get last value from named stacker
250 * @param $stackerName Name of the stack
251 * @return $value Value of last added value
252 * @throws InvalidArgumentException If a parameter is invalid
253 * @throws BadMethodCallException If the named stacker was not found
254 * @throws BadMethodCallException If the named stacker is empty
256 protected function getLastValue (string $stackerName) {
257 // Validate parameter
258 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s - CALLED!', $stackerName));
259 if (empty($stackerName)) {
260 // No empty stack name
261 throw new InvalidArgumentException('Parameter "stackerName" is empty');
262 } elseif (!$this->isStackInitialized($stackerName)) {
263 // Throw an exception
264 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), self::EXCEPTION_NO_STACKER_FOUND);
265 } elseif ($this->isStackEmpty($stackerName)) {
266 // Throw an exception
267 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), self::EXCEPTION_STACKER_IS_EMPTY);
270 // Calculate last index
271 $lastIndex = $this->getStackCount($stackerName) - 1;
273 // Now get the last value
274 $value = $this->getGenericArrayElement('stacks', $stackerName, 'entries', $lastIndex);
277 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: value[]=%s - EXIT!', gettype($value)));
282 * Get first value from named stacker
284 * @param $stackerName Name of the stack
285 * @return $value Value of last added value
286 * @throws InvalidArgumentException If a parameter is invalid
287 * @throws BadMethodCallException If the named stacker was not found
288 * @throws BadMethodCallException If the named stacker is empty
290 protected function getFirstValue (string $stackerName) {
291 // Validate parameter
292 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s - CALLED!', $stackerName));
293 if (empty($stackerName)) {
294 // No empty stack name
295 throw new InvalidArgumentException('Parameter "stackerName" is empty');
296 } elseif (!$this->isStackInitialized($stackerName)) {
297 // Throw an exception
298 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), self::EXCEPTION_NO_STACKER_FOUND);
299 } elseif ($this->isStackEmpty($stackerName)) {
300 // Throw an exception
301 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), self::EXCEPTION_STACKER_IS_EMPTY);
304 // Now get the first value
305 $value = $this->getGenericArrayElement('stacks', $stackerName, 'entries', '0');
308 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: value[]=%s - EXIT!', gettype($value)));
313 * "Pops" last entry from stack
315 * @param $stackerName Name of the stack
316 * @return $value Value "poped" from array
317 * @throws InvalidArgumentException If a parameter is invalid
318 * @throws BadMethodCallException If the named stacker was not found
319 * @throws BadMethodCallException If the named stacker is empty
321 protected function popLast (string $stackerName) {
322 // Validate parameter
323 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s - CALLED!', $stackerName));
324 if (empty($stackerName)) {
325 // No empty stack name
326 throw new InvalidArgumentException('Parameter "stackerName" is empty');
327 } elseif (!$this->isStackInitialized($stackerName)) {
328 // Throw an exception
329 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), self::EXCEPTION_NO_STACKER_FOUND);
330 } elseif ($this->isStackEmpty($stackerName)) {
331 // Throw an exception
332 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), self::EXCEPTION_STACKER_IS_EMPTY);
335 // Now, remove the last entry, we don't care about the return value here, see elseif() block above
336 $value = $this->popGenericArrayElement('stacks', $stackerName, 'entries');
339 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: value[]=%s - EXIT!', gettype($value)));
344 * "Pops" first entry from stack
346 * @param $stackerName Name of the stack
347 * @return $value Value "shifted" from array
348 * @throws InvalidArgumentException If a parameter is invalid
349 * @throws BadMethodCallException If the named stacker was not found
350 * @throws BadMethodCallException If the named stacker is empty
352 protected function popFirst (string $stackerName) {
353 // Validate parameter
354 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s - CALLED!', $stackerName));
355 if (empty($stackerName)) {
356 // No empty stack name
357 throw new InvalidArgumentException('Parameter "stackerName" is empty');
358 } elseif (!$this->isStackInitialized($stackerName)) {
359 // Throw an exception
360 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), self::EXCEPTION_NO_STACKER_FOUND);
361 } elseif ($this->isStackEmpty($stackerName)) {
362 // Throw an exception
363 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), self::EXCEPTION_STACKER_IS_EMPTY);
366 // Now, remove the last entry, we don't care about the return value here, see elseif() block above
367 $value = $this->shiftGenericArrayElement('stacks', $stackerName, 'entries');
370 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: value[]=%s - EXIT!', gettype($value)));