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 AlreadyInitializedStackerException 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 // @TODO Change to BMCE
77 throw new AlreadyInitializedStackerException(array($this, $stackerName, $forceReInit), self::EXCEPTION_STACKER_ALREADY_INITIALIZED);
80 // Initialize the given stack and "cache" configuration entry
81 $this->initGenericArrayKey('stacks', $stackerName, 'entries', $forceReInit);
82 $this->cachedMaxStackSizes[$stackerName] = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry(sprintf('stacker_%s_max_size', $stackerName));
85 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-STACKER: EXIT!');
89 * Initializes all stacks
92 * @throws InvalidArgumentException If a parameter is invalid
94 public function initStacks (array $stacks, bool $forceReInit = false) {
96 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stacks()=%d,forceReInit=%d - CALLED!', count($stacks), intval($forceReInit)));
97 if (count($stacks) == 0) {
98 // No empty stack name
99 throw new InvalidArgumentException('Array "stacks" is empty');
102 // "Walk" through all (more will be added as needed
103 foreach ($stacks as $stackerName) {
105 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: Calling this->initStack(%s,%d) ...', $stackerName, intval($forceReInit)));
106 $this->initStack($stackerName, $forceReInit);
110 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-STACKER: EXIT!');
114 * Checks whether the given stack is initialized (set in array $stackers)
116 * @param $stackerName Name of the stack
117 * @return $isInitialized Whether the stack is initialized
118 * @throws InvalidArgumentException If a parameter is invalid
120 public function isStackInitialized (string $stackerName) {
121 // Validate parameter
122 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s - CALLED!', $stackerName));
123 if (empty($stackerName)) {
124 // No empty stack name
125 throw new InvalidArgumentException('Parameter "stackerName" is empty');
129 $isInitialized = ($this->isValidGenericArrayKey('stacks', $stackerName, 'entries'));
132 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: isInitialized=%d - EXIT!', intval($isInitialized)));
133 return $isInitialized;
137 * Checks whether the given stack is full
139 * @param $stackerName Name of the stack
140 * @return $isFull Whether the stack is full
141 * @throws InvalidArgumentException If a parameter is invalid
142 * @throws BadMethodCallException If given stack is missing
144 protected function isStackFull (string $stackerName) {
145 // Validate parameter
146 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s - CALLED!', $stackerName));
147 if (empty($stackerName)) {
148 // No empty stack name
149 throw new InvalidArgumentException('Parameter "stackerName" is empty');
150 } elseif (!$this->isStackInitialized($stackerName)) {
151 // Throw an exception
152 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), self::EXCEPTION_NO_STACKER_FOUND);
155 // So, is the stack full?
156 $isFull = (($this->getStackCount($stackerName)) == $this->cachedMaxStackSizes[$stackerName]);
159 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: isFull=%d - EXIT!', intval($isFull)));
164 * Checks whether the given stack is empty
166 * @param $stackerName Name of the stack
167 * @return $isEmpty Whether the stack is empty
168 * @throws InvalidArgumentException If a parameter is invalid
169 * @throws BadMethodCallException If given stack is missing
171 public function isStackEmpty (string $stackerName) {
172 // Validate parameter
173 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s - CALLED!', $stackerName));
174 if (empty($stackerName)) {
175 // No empty stack name
176 throw new InvalidArgumentException('Parameter "stackerName" is empty');
177 } elseif (!$this->isStackInitialized($stackerName)) {
178 // Throw an exception
179 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), self::EXCEPTION_NO_STACKER_FOUND);
182 // So, is the stack empty?
183 $isEmpty = (($this->getStackCount($stackerName)) == 0);
186 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: isEmpty=%d - EXIT!', intval($isEmpty)));
191 * Getter for size of given stack (array count)
193 * @param $stackerName Name of the stack
194 * @return $count Size of stack (array count)
195 * @throws InvalidArgumentException If a parameter is invalid
196 * @throws BadMethodCallException If given stack is missing
198 public function getStackCount (string $stackerName) {
199 // Validate parameter
200 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s - CALLED!', $stackerName));
201 if (empty($stackerName)) {
202 // No empty stack name
203 throw new InvalidArgumentException('Parameter "stackerName" is empty');
204 } elseif (!$this->isStackInitialized($stackerName)) {
205 // Throw an exception
206 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), self::EXCEPTION_NO_STACKER_FOUND);
209 // Now, count the array of entries
210 $count = $this->countGenericArrayElements('stacks', $stackerName, 'entries');
213 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: count=%d - EXIT!', $count));
218 * Adds a value to given stack
220 * @param $stackerName Name of the stack
221 * @param $value Value to add to this stacker
223 * @throws InvalidArgumentException If a parameter is invalid
224 * @throws BadMethodCallException If given stack is missing
225 * @throws FullStackerException Thrown if the stack is full
227 protected function addValueToStack (string $stackerName, $value) {
228 // Validate parameter
229 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s,value[]=%s - CALLED!', $stackerName, gettype($value)));
230 if (empty($stackerName)) {
231 // No empty stack name
232 throw new InvalidArgumentException('Parameter "stackerName" is empty');
233 } elseif (!$this->isStackInitialized($stackerName)) {
234 // Throw an exception
235 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), self::EXCEPTION_NO_STACKER_FOUND);
236 } elseif ($this->isStackFull($stackerName)) {
238 throw new FullStackerException([$this, $stackerName, $value], self::EXCEPTION_STACKER_IS_FULL);
241 // Now add the value to the stack
242 $this->pushValueToGenericArrayKey('stacks', $stackerName, 'entries', $value);
245 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput('BASE-STACKER: EXIT!');
249 * Get last value from named stacker
251 * @param $stackerName Name of the stack
252 * @return $value Value of last added value
253 * @throws InvalidArgumentException If a parameter is invalid
254 * @throws BadMethodCallException If the named stacker was not found
255 * @throws BadMethodCallException If the named stacker is empty
257 protected function getLastValue (string $stackerName) {
258 // Validate parameter
259 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s - CALLED!', $stackerName));
260 if (empty($stackerName)) {
261 // No empty stack name
262 throw new InvalidArgumentException('Parameter "stackerName" is empty');
263 } elseif (!$this->isStackInitialized($stackerName)) {
264 // Throw an exception
265 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), self::EXCEPTION_NO_STACKER_FOUND);
266 } elseif ($this->isStackEmpty($stackerName)) {
267 // Throw an exception
268 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), self::EXCEPTION_STACKER_IS_EMPTY);
271 // Now get the last value
272 $value = $this->getGenericArrayElement('stacks', $stackerName, 'entries', $this->getStackCount($stackerName) - 1);
275 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: value[]=%s - EXIT!', gettype($value)));
280 * Get first value from named stacker
282 * @param $stackerName Name of the stack
283 * @return $value Value of last added value
284 * @throws InvalidArgumentException If a parameter is invalid
285 * @throws BadMethodCallException If the named stacker was not found
286 * @throws BadMethodCallException If the named stacker is empty
288 protected function getFirstValue (string $stackerName) {
289 // Validate parameter
290 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s - CALLED!', $stackerName));
291 if (empty($stackerName)) {
292 // No empty stack name
293 throw new InvalidArgumentException('Parameter "stackerName" is empty');
294 } elseif (!$this->isStackInitialized($stackerName)) {
295 // Throw an exception
296 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), self::EXCEPTION_NO_STACKER_FOUND);
297 } elseif ($this->isStackEmpty($stackerName)) {
298 // Throw an exception
299 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), self::EXCEPTION_STACKER_IS_EMPTY);
302 // Now get the first value
303 $value = $this->getGenericArrayElement('stacks', $stackerName, 'entries', 0);
306 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: value[]=%s - EXIT!', gettype($value)));
311 * "Pops" last entry from stack
313 * @param $stackerName Name of the stack
314 * @return $value Value "poped" from array
315 * @throws InvalidArgumentException If a parameter is invalid
316 * @throws BadMethodCallException If the named stacker was not found
317 * @throws BadMethodCallException If the named stacker is empty
319 protected function popLast (string $stackerName) {
320 // Validate parameter
321 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s - CALLED!', $stackerName));
322 if (empty($stackerName)) {
323 // No empty stack name
324 throw new InvalidArgumentException('Parameter "stackerName" is empty');
325 } elseif (!$this->isStackInitialized($stackerName)) {
326 // Throw an exception
327 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), self::EXCEPTION_NO_STACKER_FOUND);
328 } elseif ($this->isStackEmpty($stackerName)) {
329 // Throw an exception
330 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), self::EXCEPTION_STACKER_IS_EMPTY);
333 // Now, remove the last entry, we don't care about the return value here, see elseif() block above
334 $value = $this->popGenericArrayElement('stacks', $stackerName, 'entries');
337 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: value[]=%s - EXIT!', gettype($value)));
342 * "Pops" first entry from stack
344 * @param $stackerName Name of the stack
345 * @return $value Value "shifted" from array
346 * @throws InvalidArgumentException If a parameter is invalid
347 * @throws BadMethodCallException If the named stacker was not found
348 * @throws BadMethodCallException If the named stacker is empty
350 protected function popFirst (string $stackerName) {
351 // Validate parameter
352 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: stackerName=%s - CALLED!', $stackerName));
353 if (empty($stackerName)) {
354 // No empty stack name
355 throw new InvalidArgumentException('Parameter "stackerName" is empty');
356 } elseif (!$this->isStackInitialized($stackerName)) {
357 // Throw an exception
358 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), self::EXCEPTION_NO_STACKER_FOUND);
359 } elseif ($this->isStackEmpty($stackerName)) {
360 // Throw an exception
361 throw new BadMethodCallException(sprintf('stackerName=%s not yet initialized but method called.', $stackerName), self::EXCEPTION_STACKER_IS_EMPTY);
364 // Now, remove the last entry, we don't care about the return value here, see elseif() block above
365 $value = $this->shiftGenericArrayElement('stacks', $stackerName, 'entries');
368 //* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugOutput(sprintf('BASE-STACKER: value[]=%s - EXIT!', gettype($value)));