]> git.mxchange.org Git - core.git/blob - framework/main/classes/class_BaseFrameworkSystem.php
3920db4cfd52c89dbf75b43bbc79643128bce04f
[core.git] / framework / main / classes / class_BaseFrameworkSystem.php
1 <?php
2 // Own namespace
3 namespace Org\Mxchange\CoreFramework\Object;
4
5 // Import framework stuff
6 use Org\Mxchange\CoreFramework\Bootstrap\FrameworkBootstrap;
7 use Org\Mxchange\CoreFramework\EntryPoint\ApplicationEntryPoint;
8 use Org\Mxchange\CoreFramework\Factory\Object\ObjectFactory;
9 use Org\Mxchange\CoreFramework\Filesystem\FileIoException;
10 use Org\Mxchange\CoreFramework\Filesystem\PathWriteProtectedException;
11 use Org\Mxchange\CoreFramework\Generic\FrameworkInterface;
12 use Org\Mxchange\CoreFramework\Generic\NullPointerException;
13 use Org\Mxchange\CoreFramework\Generic\UnsupportedOperationException;
14 use Org\Mxchange\CoreFramework\Helper\Application\ApplicationHelper;
15 use Org\Mxchange\CoreFramework\Loader\ClassLoader;
16 use Org\Mxchange\CoreFramework\Manager\ManageableApplication;
17 use Org\Mxchange\CoreFramework\Middleware\Debug\DebugMiddleware;
18 use Org\Mxchange\CoreFramework\Registry\GenericRegistry;
19 use Org\Mxchange\CoreFramework\Result\Database\CachedDatabaseResult;
20 use Org\Mxchange\CoreFramework\State\Stateable;
21 use Org\Mxchange\CoreFramework\Stream\Output\OutputStreamer;
22 use Org\Mxchange\CoreFramework\Utils\Strings\StringUtils;
23
24 // Import SPL stuff
25 use \BadMethodCallException;
26 use \InvalidArgumentException;
27 use \ReflectionClass;
28 use \SplFileInfo;
29 use \stdClass;
30
31 /**
32  * The simulator system class is the super class of all other classes. This
33  * class handles saving of games etc.
34  *
35  * @author              Roland Haeder <webmaster@shipsimu.org>
36  * @version             0.0.0
37  * @copyright   Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2023 Core Developer Team
38  * @license             GNU GPL 3.0 or any newer version
39  * @link                http://www.shipsimu.org
40  *
41  * This program is free software: you can redistribute it and/or modify
42  * it under the terms of the GNU General Public License as published by
43  * the Free Software Foundation, either version 3 of the License, or
44  * (at your option) any later version.
45  *
46  * This program is distributed in the hope that it will be useful,
47  * but WITHOUT ANY WARRANTY; without even the implied warranty of
48  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
49  * GNU General Public License for more details.
50  *
51  * You should have received a copy of the GNU General Public License
52  * along with this program. If not, see <http://www.gnu.org/licenses/>.
53  */
54 abstract class BaseFrameworkSystem extends stdClass implements FrameworkInterface {
55         /**
56          * Self-referencing instance
57          */
58         private static $selfInstance = NULL;
59
60         /**
61          * Debug instance
62          */
63         private static $debugInstance = NULL;
64
65         /**
66          * Stub methods
67          */
68         private static $stubMethods = [
69                 'partialStub' => true,
70                 '__call' => true,
71                 '__callStatic' => true,
72         ];
73
74         /**
75          * The real class name
76          */
77         private $realClass = __CLASS__;
78
79         /**
80          * Call-back instance
81          */
82         private $callbackInstance = NULL;
83
84         /**
85          * Generic array
86          */
87         private $genericArray = [];
88
89         /***********************
90          * Exception codes.... *
91          ***********************/
92
93         // @todo Try to clean these constants up
94         const EXCEPTION_IS_NULL_POINTER              = 0x001;
95         const EXCEPTION_IS_NO_OBJECT                 = 0x002;
96         const EXCEPTION_IS_NO_ARRAY                  = 0x003;
97         const EXCEPTION_MISSING_METHOD               = 0x004;
98         const EXCEPTION_CLASSES_NOT_MATCHING         = 0x005;
99         const EXCEPTION_INDEX_OUT_OF_BOUNDS          = 0x006;
100         const EXCEPTION_DIMENSION_ARRAY_INVALID      = 0x007;
101         const EXCEPTION_ITEM_NOT_TRADEABLE           = 0x008;
102         const EXCEPTION_ITEM_NOT_IN_PRICE_LIST       = 0x009;
103         const EXCEPTION_GENDER_IS_WRONG              = 0x00a;
104         const EXCEPTION_BIRTH_DATE_IS_INVALID        = 0x00b;
105         const EXCEPTION_EMPTY_STRUCTURES_ARRAY       = 0x00c;
106         const EXCEPTION_HAS_ALREADY_PERSONELL_LIST   = 0x00d;
107         const EXCEPTION_NOT_ENOUGTH_UNEMPLOYEES      = 0x00e;
108         const EXCEPTION_TOTAL_PRICE_NOT_CALCULATED   = 0x00f;
109         const EXCEPTION_HARBOR_HAS_NO_SHIPYARDS      = 0x010;
110         const EXCEPTION_CONTRACT_PARTNER_INVALID     = 0x011;
111         const EXCEPTION_CONTRACT_PARTNER_MISMATCH    = 0x012;
112         const EXCEPTION_CONTRACT_ALREADY_SIGNED      = 0x013;
113         const EXCEPTION_UNEXPECTED_EMPTY_STRING      = 0x014;
114         const EXCEPTION_PATH_NOT_FOUND               = 0x015;
115         const EXCEPTION_INVALID_PATH_NAME            = 0x016;
116         const EXCEPTION_READ_PROTECED_PATH           = 0x017;
117         const EXCEPTION_WRITE_PROTECED_PATH          = 0x018;
118         const EXCEPTION_DIR_POINTER_INVALID          = 0x019;
119         const EXCEPTION_FILE_POINTER_INVALID         = 0x01a;
120         const EXCEPTION_INVALID_RESOURCE             = 0x01b;
121         const EXCEPTION_UNEXPECTED_OBJECT            = 0x01c;
122         const EXCEPTION_LIMIT_ELEMENT_IS_UNSUPPORTED = 0x01d;
123         const EXCEPTION_GETTER_IS_MISSING            = 0x01e;
124         const EXCEPTION_ARRAY_EXPECTED               = 0x01f;
125         const EXCEPTION_ARRAY_HAS_INVALID_COUNT      = 0x020;
126         const EXCEPTION_ID_IS_INVALID_FORMAT         = 0x021;
127         const EXCEPTION_MD5_CHECKSUMS_MISMATCH       = 0x022;
128         const EXCEPTION_UNEXPECTED_STRING_SIZE       = 0x023;
129         const EXCEPTION_SIMULATOR_ID_INVALID         = 0x024;
130         const EXCEPTION_MISMATCHING_COMPRESSORS      = 0x025;
131         const EXCEPTION_CONTAINER_ITEM_IS_NULL       = 0x026;
132         const EXCEPTION_ITEM_IS_NO_ARRAY             = 0x027;
133         const EXCEPTION_CONTAINER_MAYBE_DAMAGED      = 0x028;
134         const EXCEPTION_INVALID_STRING               = 0x029;
135         const EXCEPTION_VARIABLE_NOT_SET             = 0x02a;
136         const EXCEPTION_ATTRIBUTES_ARE_MISSING       = 0x02b;
137         const EXCEPTION_ARRAY_ELEMENTS_MISSING       = 0x02c;
138         const EXCEPTION_TEMPLATE_ENGINE_UNSUPPORTED  = 0x02d;
139         const EXCEPTION_FACTORY_REQUIRE_PARAMETER    = 0x02e;
140         const EXCEPTION_MISSING_ELEMENT              = 0x02f;
141         const EXCEPTION_HEADERS_ALREADY_SENT         = 0x030;
142         const EXCEPTION_DEFAULT_CONTROLLER_GONE      = 0x031;
143         const EXCEPTION_CLASS_NOT_FOUND              = 0x032;
144         const EXCEPTION_REQUIRED_INTERFACE_MISSING   = 0x033;
145         const EXCEPTION_FATAL_ERROR                  = 0x034;
146         const EXCEPTION_FILE_NOT_FOUND               = 0x035;
147         const EXCEPTION_ASSERTION_FAILED             = 0x036;
148         const EXCEPTION_FILE_NOT_REACHABLE           = 0x037;
149         const EXCEPTION_FILE_CANNOT_BE_READ          = 0x038;
150         const EXCEPTION_FILE_CANNOT_BE_WRITTEN       = 0x039;
151         const EXCEPTION_PATH_CANNOT_BE_WRITTEN       = 0x03a;
152         const EXCEPTION_DATABASE_UPDATED_NOT_ALLOWED = 0x03b;
153         const EXCEPTION_FILTER_CHAIN_INTERCEPTED     = 0x03c;
154         const EXCEPTION_INVALID_SOCKET               = 0x03d;
155         const EXCEPTION_SELF_INSTANCE                = 0x03e;
156
157         /**
158          * Startup time in miliseconds
159          */
160         private static $startupTime = 0;
161
162         /**
163          * Protected super constructor
164          *
165          * @param       $className      Name of the class
166          * @return      void
167          */
168         protected function __construct (string $className) {
169                 // Set real class
170                 $this->setRealClass($className);
171
172                 // Is the startup time set? (0 cannot be true anymore)
173                 if (self::$startupTime == 0) {
174                         // Then set it
175                         self::$startupTime = microtime(true);
176                 }
177         }
178
179         /**
180          * Destructor for all classes. You should not call this method on your own.
181          *
182          * @return      void
183          */
184         public function __destruct () {
185                 // Is this object already destroyed?
186                 if ($this->__toString() != 'DestructedObject') {
187                         // Destroy all informations about this class but keep some text about it alive
188                         $this->setRealClass('DestructedObject');
189                 } elseif ((defined('DEBUG_DESTRUCTOR')) && (is_object($this->getDebugInstance()))) {
190                         // Already destructed object
191                         self::createDebugInstance(__CLASS__, __LINE__)->warningMessage(sprintf('The object <span class="object_name">%s</span> is already destroyed.',
192                                 $this->__toString()
193                         ));
194                 } else {
195                         // Do not call this twice
196                         trigger_error(__METHOD__ . ': Called twice.');
197                         exit(255);
198                 }
199         }
200
201         /**
202          * The __call() method where all non-implemented methods end up
203          *
204          * @param       $methodName             Name of the missing method
205          * @args        $args                   Arguments passed to the method
206          * @return      void
207          */
208         public final function __call (string $methodName, array $args = NULL) {
209                 // Set self-instance
210                 self::$selfInstance = $this;
211
212                 // Call static method
213                 self::__callStatic($methodName, $args);
214
215                 // Clear self-instance
216                 self::$selfInstance = NULL;
217         }
218
219         /**
220          * The __callStatic() method where all non-implemented static methods end up
221          *
222          * @param       $methodName             Name of the missing method
223          * @param       $args                   Arguments passed to the method
224          * @return      void
225          * @throws      InvalidArgumentException If self::$selfInstance is not a framework's own object
226          */
227         public static final function __callStatic (string $methodName, array $args = NULL) {
228                 // Init argument string and class name
229                 //* PRINT-DEBUG: */ printf('[%s:%d]: methodName=%s,args[]=%s - CALLED!' . PHP_EOL, __METHOD__, __LINE__, $methodName, gettype($args));
230                 $argsString = '';
231                 $className = 'unknown';
232
233                 // Is self-instance set?
234                 if (self::$selfInstance instanceof FrameworkInterface) {
235                         // Framework's own instance
236                         $className = self::$selfInstance->__toString();
237                 } elseif (!is_null(self::$selfInstance)) {
238                         // Invalid argument!
239                         throw new InvalidArgumentException(sprintf('self::instance[%s] is not expected.', gettype(self::$selfInstance)), self::EXCEPTION_SELF_INSTANCE);
240                 }
241
242                 // Is it NULL, empty or an array?
243                 if (is_null($args)) {
244                         // No arguments
245                         $argsString = 'NULL';
246                 } elseif (is_array($args)) {
247                         // Start braces
248                         $argsString = '(';
249
250                         // Some arguments are there
251                         foreach ($args as $arg) {
252                                 // Add data about the argument
253                                 $argsString .= gettype($arg) . ':';
254
255                                 if (is_null($arg)) {
256                                         // Found a NULL argument
257                                         $argsString .= 'NULL';
258                                 } elseif (is_string($arg)) {
259                                         // Add length for strings
260                                         $argsString .= strlen($arg);
261                                 } elseif ((is_int($arg)) || (is_float($arg))) {
262                                         // ... integer/float
263                                         $argsString .= $arg;
264                                 } elseif (is_array($arg)) {
265                                         // .. or size if array
266                                         $argsString .= count($arg);
267                                 } elseif (is_object($arg)) {
268                                         // Get reflection
269                                         $reflection = new ReflectionClass($arg);
270
271                                         // Is an other object, maybe no __toString() available
272                                         $argsString .= $reflection->getName();
273                                 } elseif ($arg === true) {
274                                         // ... is boolean 'true'
275                                         $argsString .= 'true';
276                                 } elseif ($arg === false) {
277                                         // ... is boolean 'false'
278                                         $argsString .= 'false';
279                                 }
280
281                                 // Comma for next one
282                                 $argsString .= ', ';
283                         }
284
285                         // Last comma found?
286                         if (substr($argsString, -2, 1) == ',') {
287                                 // Remove last comma
288                                 $argsString = substr($argsString, 0, -2);
289                         }
290
291                         // Close braces
292                         $argsString .= ')';
293                 }
294
295                 // Output stub message
296                 // @TODO __CLASS__ does always return BaseFrameworkSystem but not the extending (=child) class
297                 self::createDebugInstance(__CLASS__, __LINE__)->warningMessage(sprintf('[%s::%s]: Stub! Args: %s',
298                         $className,
299                         $methodName,
300                         $argsString
301                 ));
302
303                 // Return nothing
304                 return NULL;
305         }
306
307         /**
308          * Getter for $realClass
309          *
310          * @return      $realClass The name of the real class (not BaseFrameworkSystem)
311          */
312         public function __toString () {
313                 return $this->realClass;
314         }
315
316         /**
317          * Magic method to catch setting of missing but set class fields/attributes
318          *
319          * @param       $name   Name of the field/attribute
320          * @param       $value  Value to store
321          * @return      void
322          */
323         public final function __set (string $name, $value) {
324                 $this->debugBackTrace(sprintf('Tried to set a missing field. name=%s, value[%s]=%s',
325                         $name,
326                         gettype($value),
327                         print_r($value, true)
328                 ));
329         }
330
331         /**
332          * Magic method to catch getting of missing fields/attributes
333          *
334          * @param       $name   Name of the field/attribute
335          * @return      void
336          */
337         public final function __get (string $name) {
338                 $this->debugBackTrace(sprintf('Tried to get a missing field. name=%s',
339                         $name
340                 ));
341         }
342
343         /**
344          * Magic method to catch unsetting of missing fields/attributes
345          *
346          * @param       $name   Name of the field/attribute
347          * @return      void
348          */
349         public final function __unset (string $name) {
350                 $this->debugBackTrace(sprintf('Tried to unset a missing field. name=%s',
351                         $name
352                 ));
353         }
354
355         /**
356          * Magic method to catch object serialization
357          *
358          * @return      $unsupported    Unsupported method
359          * @throws      UnsupportedOperationException   Objects of this framework cannot be serialized
360          */
361         public final function __sleep () {
362                 throw new UnsupportedOperationException([$this, __FUNCTION__], FrameworkInterface::EXCEPTION_UNSPPORTED_OPERATION);
363         }
364
365         /**
366          * Magic method to catch object deserialization
367          *
368          * @return      $unsupported    Unsupported method
369          * @throws      UnsupportedOperationException   Objects of this framework cannot be serialized
370          */
371         public final function __wakeup () {
372                 throw new UnsupportedOperationException([$this, __FUNCTION__], FrameworkInterface::EXCEPTION_UNSPPORTED_OPERATION);
373         }
374
375         /**
376          * Magic method to catch calls when an object instance is called
377          *
378          * @return      $unsupported    Unsupported method
379          * @throws      UnsupportedOperationException   Objects of this framework cannot be serialized
380          */
381         public final function __invoke () {
382                 throw new UnsupportedOperationException([$this, __FUNCTION__], FrameworkInterface::EXCEPTION_UNSPPORTED_OPERATION);
383         }
384
385         /**
386          * Setter for the real class name
387          *
388          * @param       $realClass      Class name (string)
389          * @return      void
390          */
391         public final function setRealClass (string $realClass) {
392                 // Set real class
393                 $this->realClass = $realClass;
394         }
395
396         /**
397          * Setter for debug instance
398          *
399          * @param       $debugInstance  The instance for debug output class
400          * @return      void
401          */
402         public final function setDebugInstance (DebugMiddleware $debugInstance) {
403                 GenericRegistry::getRegistry()->addInstance('debug', $debugInstance);
404         }
405
406         /**
407          * Getter for debug instance
408          *
409          * @return      $debugInstance  Instance to class DebugConsoleOutput or DebugWebOutput
410          */
411         public final function getDebugInstance () {
412                 return self::$debugInstance;
413         }
414
415         /**
416          * Setter for web output instance
417          *
418          * @param       $webInstance    The instance for web output class
419          * @return      void
420          */
421         public final function setWebOutputInstance (OutputStreamer $webInstance) {
422                 GenericRegistry::getRegistry()->addInstance('web_output', $webInstance);
423         }
424
425         /**
426          * Getter for web output instance
427          *
428          * @return      $webOutputInstance - Instance to class WebOutput
429          */
430         public final function getWebOutputInstance () {
431                 return GenericRegistry::getRegistry()->getInstance('web_output');
432         }
433
434         /**
435          * Setter for call-back instance
436          *
437          * @param       $callbackInstance       An instance of a FrameworkInterface class
438          * @return      void
439          */
440         public final function setCallbackInstance (FrameworkInterface $callbackInstance) {
441                 $this->callbackInstance = $callbackInstance;
442         }
443
444         /**
445          * Getter for call-back instance
446          *
447          * @return      $callbackInstance       An instance of a FrameworkInterface class
448          */
449         protected final function getCallbackInstance () {
450                 return $this->callbackInstance;
451         }
452
453         /**
454          * Checks whether an object equals this object. You should overwrite this
455          * method to implement own equality checks
456          *
457          * @param       $objectInstance         An instance of a FrameworkInterface object
458          * @return      $equals                         Whether both objects equals
459          */
460         public function equals (FrameworkInterface $objectInstance) {
461                 // Now test it
462                 $equals = ((
463                         $this->__toString() == $objectInstance->__toString()
464                 ) && (
465                         $this->hashCode() == $objectInstance->hashCode()
466                 ));
467
468                 // Return the result
469                 return $equals;
470         }
471
472         /**
473          * Generates a generic hash code of this class. You should really overwrite
474          * this method with your own hash code generator code. But keep KISS in mind.
475          *
476          * @return      $hashCode       A generic hash code respresenting this whole class
477          */
478         public function hashCode () {
479                 // Simple hash code
480                 return crc32($this->__toString());
481         }
482
483         /**
484          * Appends a trailing slash to a string
485          *
486          * @param       $str    A string (maybe) without trailing slash
487          * @return      $str    A string with an auto-appended trailing slash
488          */
489         public final function addMissingTrailingSlash (string $str) {
490                 // Is there a trailing slash?
491                 if (substr($str, -1, 1) != '/') {
492                         $str .= '/';
493                 }
494
495                 // Return string with trailing slash
496                 return $str;
497         }
498
499         /**
500          * Debugs this instance by putting out it's full content
501          *
502          * @param       $message        Optional message to show in debug output
503          * @return      void
504          */
505         public final function debugInstance (string $message = '') {
506                 // Restore the error handler to avoid trouble with missing array elements or undeclared variables
507                 restore_error_handler();
508
509                 // Init content
510                 $content = '';
511
512                 // Is a message set?
513                 if (!empty($message)) {
514                         // Construct message
515                         $content = sprintf('<div class="debug_message">
516         Message: %s
517 </div>' . PHP_EOL, $message);
518                 }
519
520                 // Generate the output
521                 $content .= sprintf('<pre>%s</pre>',
522                         trim(
523                                 htmlentities(
524                                         print_r($this, true)
525                                 )
526                         )
527                 );
528
529                 // Output it
530                 ApplicationEntryPoint::exitApplication(sprintf('<div class="debug_header">
531         %s debug output:
532 </div>
533 <div class="debug_content">
534         %s
535 </div>
536 Loaded includes:
537 <div class="debug_include_list">
538         %s
539 </div>',
540                         $this->__toString(),
541                         $content,
542                         ClassLoader::getSelfInstance()->getPrintableIncludeList()
543                 ));
544         }
545
546         /**
547          * Output a partial stub message for the caller method
548          *
549          * @param       $message        An optional message to display
550          * @return      void
551          */
552         protected function partialStub (string $message = '') {
553                 // Init variable
554                 $stubMessage = 'Partial stub!';
555
556                 // Is an extra message given?
557                 if (!empty($message)) {
558                         // Then add it as well
559                         $stubMessage .= ' Message: ' . $message;
560                 }
561
562                 // Debug instance is there?
563                 if (!is_null($this->getDebugInstance())) {
564                         // Output stub message
565                         self::createDebugInstance(__CLASS__, __LINE__)->warningMessage($stubMessage);
566                 } else {
567                         // Trigger an error
568                         trigger_error($stubMessage);
569                         exit(255);
570                 }
571         }
572
573         /**
574          * Outputs a debug backtrace and stops further script execution
575          *
576          * @param       $message        An optional message to output
577          * @param       $doExit         Whether exit the program (true is default)
578          * @return      void
579          */
580         public function debugBackTrace (string $message = '', bool $doExit = true) {
581                 // Sorry, there is no other way getting this nice backtrace
582                 if (!empty($message)) {
583                         // Output message
584                         printf('Message: %s<br />' . PHP_EOL, $message);
585                 }
586
587                 print('<pre>');
588                 debug_print_backtrace();
589                 print('</pre>');
590
591                 // Exit program?
592                 if ($doExit === true) {
593                         exit();
594                 }
595         }
596
597         /**
598          * Creates an instance of a debugger instance
599          *
600          * @param       $className              Name of the class (currently unsupported)
601          * @param       $lineNumber             Line number where the call was made
602          * @return      $debugInstance  An instance of a debugger class
603          * @throws      InvalidArgumentException        If a parameter has an invalid value
604          * @deprecated  Not fully, as the new Logger facilities are not finished yet.
605          */
606         public final static function createDebugInstance (string $className, int $lineNumber = NULL) {
607                 // Validate parameter
608                 //* NOISY-DEBUG: */ printf('[%s:%d]: className=%s,lineNumber[%s]=%d - CALLED!' . PHP_EOL, __METHOD__, __LINE__, $className, gettype($lineNumber), $lineNumber);
609                 if (empty($className)) {
610                         // Throw IAE
611                         throw new InvalidArgumentException('Parameter "className" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
612                 }
613
614                 // Is the debug instance set?
615                 //* NOISY-DEBUG: */ printf('[%s:%d]: self::debugInstance[]=%s' . PHP_EOL, __METHOD__, __LINE__, gettype(self::$debugInstance));
616                 if (is_null(self::$debugInstance)) {
617                         // Init debug instance
618                         self::$debugInstance = NULL;
619
620                         // Try it
621                         try {
622                                 // Get a debugger instance
623                                 //* NOISY-DEBUG: */ printf('[%s:%d]: className=%s' . PHP_EOL, __METHOD__, __LINE__, $className);
624                                 self::$debugInstance = DebugMiddleware::createDebugMiddleware(FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('debug_' . FrameworkBootstrap::getRequestTypeFromSystem() . '_class'), $className);
625                         } catch (NullPointerException $e) {
626                                 // Didn't work, no instance there
627                                 exit(sprintf('[%s:%d]: Cannot create debugInstance! Exception=%s,message=%s,className=%s,lineNumber=%d' . PHP_EOL, __METHOD__, __LINE__, $e->__toString(), $e->getMessage(), $className, $lineNumber));
628                         }
629                 }
630
631                 // Return it
632                 //* NOISY-DEBUG: */ printf('[%s:%d]: self::debugInstance=%s - EXIT!' . PHP_EOL, __METHOD__, __LINE__, self::$debugInstance->__toString());
633                 return self::$debugInstance;
634         }
635
636         /**
637          * Simple output of a message with line-break
638          *
639          * @param       $message        Message to output
640          * @return      void
641          */
642         public function outputLine (string $message) {
643                 // Simply output it
644                 print($message . PHP_EOL);
645         }
646
647         /**
648          * Marks up the code by adding e.g. line numbers
649          *
650          * @param       $phpCode                Unmarked PHP code
651          * @return      $markedCode             Marked PHP code
652          */
653         public function markupCode (string $phpCode) {
654                 // Init marked code
655                 $markedCode = '';
656
657                 // Get last error
658                 $errorArray = error_get_last();
659
660                 // Init the code with error message
661                 if (is_array($errorArray)) {
662                         // Get error infos
663                         $markedCode = sprintf('<div id="error_header">File: <span id="error_data">%s</span>, Line: <span id="error_data">%s</span>, Message: <span id="error_data">%s</span>, Type: <span id="error_data">%s</span></div>',
664                                 basename($errorArray['file']),
665                                 $errorArray['line'],
666                                 $errorArray['message'],
667                                 $errorArray['type']
668                         );
669                 }
670
671                 // Add line number to the code
672                 foreach (explode(chr(10), $phpCode) as $lineNo => $code) {
673                         // Add line numbers
674                         $markedCode .= sprintf('<span id="code_line">%s</span>: %s' . PHP_EOL,
675                                 ($lineNo + 1),
676                                 htmlentities($code, ENT_QUOTES)
677                         );
678                 }
679
680                 // Return the code
681                 return $markedCode;
682         }
683
684         /**
685          * "Getter" for databse entry
686          *
687          * @return      $entry  An array with database entries
688          * @throws      NullPointerException    If the database result is not found
689          * @throws      InvalidDatabaseResultException  If the database result is invalid
690          * @deprecated  Monolithic method, should be moved to proper classes
691          */
692         protected final function getDatabaseEntry () {
693                 // This method is deprecated
694                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-FRAMEWORK-SYSTEM: CALLED!');
695                 $this->deprecationWarning('Monolithic method, should be moved to proper classes');
696
697                 // Is there an instance?
698                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-FRAMEWORK-SYSTEM: this->resultInstance[]=%s', gettype($this->getResultInstance())));
699                 if (!$this->getResultInstance() instanceof SearchableResult) {
700                         // Throw an exception here
701                         throw new NullPointerException($this, self::EXCEPTION_IS_NULL_POINTER);
702                 }
703
704                 // Rewind it
705                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-FRAMEWORK-SYSTEM: Invoking this->resultInstance->rewind() ...');
706                 $this->getResultInstance()->rewind();
707
708                 // Do we have an entry?
709                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-FRAMEWORK-SYSTEM: this->resultInstance->isValid()=%d', intval($this->getResultInstance()->isValid())));
710                 if ($this->getResultInstance()->valid() === false) {
711                         // @TODO Move the constant to e.g. BaseDatabaseResult when there is a non-cached database result available
712                         throw new InvalidDatabaseResultException(array($this, $this->getResultInstance()), CachedDatabaseResult::EXCEPTION_INVALID_DATABASE_RESULT);
713                 }
714
715                 // Get next entry
716                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage('BASE-FRAMEWORK-SYSTEM: Invoking this->resultInstance->next() ...');
717                 $this->getResultInstance()->next();
718
719                 // Fetch it
720                 $entry = $this->getResultInstance()->current();
721
722                 // And return it
723                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-FRAMEWORK-SYSTEM: entry[]=%s - EXIT!', gettype($entry)));
724                 return $entry;
725         }
726
727         /**
728          * Getter for field name
729          *
730          * @param       $fieldName              Field name which we shall get
731          * @return      $fieldValue             Field value from the user
732          * @throws      NullPointerException    If the result instance is null
733          * @throws      InvalidArgumentException        If a parameter is not valid
734          * @deprecated  Monolithic method, should be moved to proper classes
735          */
736         public final function getField (string $fieldName) {
737                 // Check parameter
738                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-FRAMEWORK-SYSTEM: fieldName=%s - CALLED!', $fieldName));
739                 if (empty($fieldName)) {
740                         // Throw IAE
741                         throw new InvalidArgumentException('Parameter "fieldName" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
742                 }
743
744                 // This method is deprecated
745                 $this->deprecationWarning('Monolithic method, should be moved to proper classes');
746
747                 // Default field value
748                 $fieldValue = NULL;
749
750                 // Get result instance
751                 $resultInstance = $this->getResultInstance();
752
753                 // Is this instance null?
754                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-FRAMEWORK-SYSTEM: resultInstance[]=%s', gettype($resultInstance)));
755                 if (is_null($resultInstance)) {
756                         // Then the user instance is no longer valid (expired cookies?)
757                         throw new NullPointerException($this, self::EXCEPTION_IS_NULL_POINTER);
758                 }
759
760                 // Get current array
761                 $fieldArray = $resultInstance->current();
762
763                 // Convert dashes to underscore
764                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-FRAMEWORK-SYSTEM: fieldArray()=%d', count($fieldArray)));
765                 $fieldName2 = StringUtils::convertDashesToUnderscores($fieldName);
766
767                 // Does the field exist?
768                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-FRAMEWORK-SYSTEM: fieldName2=%s', $fieldName2));
769                 if ($this->isFieldSet($fieldName)) {
770                         // Get it
771                         $fieldValue = $fieldArray[$fieldName2];
772                 } elseif (FrameworkBootstrap::getConfigurationInstance()->isEnabled('developer_mode')) {
773                         // Missing field entry, may require debugging
774                         self::createDebugInstance(__CLASS__, __LINE__)->warningMessage('BASE-FRAMEWORK-SYSTEM: fieldArray<pre>=' . print_r($fieldArray, true) . '</pre>,fieldName=' . $fieldName . ' not found!');
775                 } else {
776                         // Missing field entry, may require debugging
777                         self::createDebugInstance(__CLASS__, __LINE__)->warningMessage('BASE-FRAMEWORK-SYSTEM: fieldName=' . $fieldName . ' not found!');
778                 }
779
780                 // Return it
781                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-FRAMEWORK-SYSTEM: fieldValue[]=%s - EXIT!', gettype($fieldValue)));
782                 return $fieldValue;
783         }
784
785         /**
786          * Checks if given field is set
787          *
788          * @param       $fieldName      Field name to check
789          * @return      $isSet          Whether the given field name is set
790          * @throws      NullPointerException    If the result instance is null
791          * @throws      InvalidArgumentException        If a parameter is not valid
792          */
793         public function isFieldSet (string $fieldName) {
794                 // Check parameter
795                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-FRAMEWORK-SYSTEM: fieldName=%s - CALLED!', $fieldName));
796                 if (empty($fieldName)) {
797                         // Throw IAE
798                         throw new InvalidArgumentException('Parameter "fieldName" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
799                 }
800
801                 // Get result instance
802                 $resultInstance = $this->getResultInstance();
803
804                 // Is this instance null?
805                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-FRAMEWORK-SYSTEM: resultInstance[]=%s', gettype($resultInstance)));
806                 if (is_null($resultInstance)) {
807                         // Then the user instance is no longer valid (expired cookies?)
808                         throw new NullPointerException($this, self::EXCEPTION_IS_NULL_POINTER);
809                 }
810
811                 // Get current array
812                 $fieldArray = $resultInstance->current();
813
814                 // Convert dashes to underscore
815                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-FRAMEWORK-SYSTEM: fieldArray()=%d,fieldName=%s - BEFORE!', count($fieldArray), $fieldName));
816                 $fieldName = StringUtils::convertDashesToUnderscores($fieldName);
817
818                 // Determine it
819                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->debugMessage(sprintf('BASE-FRAMEWORK-SYSTEM: fieldName=%s - AFTER!', $fieldName));
820                 $isset = isset($fieldArray[$fieldName]);
821
822                 // Return result
823                 /* NOISY-DEBUG: */ self::createDebugInstance(__CLASS__, __LINE__)->traceMessage(sprintf('BASE-FRAMEWORK-SYSTEM: isset=%d - EXIT!', intval($isset)));
824                 return $isset;
825         }
826
827         /**
828          * Outputs a deprecation warning to the developer.
829          *
830          * @param       $message        The message we shall output to the developer
831          * @return      void
832          * @todo        Write a logging mechanism for productive mode
833          */
834         public function deprecationWarning (string $message) {
835                 // Is developer mode active?
836                 if (FrameworkBootstrap::getConfigurationInstance()->isEnabled('developer_mode')) {
837                         // Debug instance is there?
838                         if (!is_null($this->getDebugInstance())) {
839                                 // Output stub message
840                                 self::createDebugInstance(__CLASS__, __LINE__)->warningMessage($message);
841                         } else {
842                                 // Trigger an error
843                                 trigger_error($message . "<br />\n");
844                                 exit(255);
845                         }
846                 } else {
847                         // @TODO Finish this part!
848                         $this->partialStub('Developer mode inactive. Message:' . $message);
849                 }
850         }
851
852         /**
853          * Checks whether the given PHP extension is loaded
854          *
855          * @param       $phpExtension   The PHP extension we shall check
856          * @return      $isLoaded       Whether the PHP extension is loaded
857          * @throws      InvalidArgumentException        If a parameter is not valid
858          */
859         public final function isPhpExtensionLoaded (string $phpExtension) {
860                 // Check parameter
861                 if (empty($phpExtension)) {
862                         // Throw IAE
863                         throw new InvalidArgumentException('Parameter "phpExtension" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
864                 }
865
866                 // Is it loaded?
867                 $isLoaded = in_array($phpExtension, get_loaded_extensions());
868
869                 // Return result
870                 return $isLoaded;
871         }
872
873         /**
874          * "Getter" as a time() replacement but with milliseconds. You should use this
875          * method instead of the encapsulated getimeofday() function.
876          *
877          * @return      $milliTime      Timestamp with milliseconds
878          */
879         public function getMilliTime () {
880                 // Get the time of day as float
881                 $milliTime = gettimeofday(true);
882
883                 // Return it
884                 return $milliTime;
885         }
886
887         /**
888          * Idles (sleeps) for given milliseconds
889          *
890          * @return      $hasSlept       Whether it goes fine
891          * @throws      InvalidArgumentException        If a parameter is not valid
892          */
893         public function idle (int $milliSeconds) {
894                 // Check parameter
895                 if ($milliSeconds < 1) {
896                         // Throw IAE
897                         throw new InvalidArgumentException(sprintf('milliSeconds=%d are not a reasonable value to idle', $milliSeconds));
898                 }
899
900                 // Sleep is fine by default
901                 $hasSlept = true;
902
903                 // Idle so long with found function
904                 if (function_exists('time_sleep_until')) {
905                         // Get current time and add idle time
906                         $sleepUntil = $this->getMilliTime() + abs($milliSeconds) / 1000;
907
908                         // New PHP 5.1.0 function found, ignore errors
909                         $hasSlept = @time_sleep_until($sleepUntil);
910                 } else {
911                         /*
912                          * My Sun station doesn't have that function even with latest PHP
913                          * package. :(
914                          */
915                         usleep($milliSeconds * 1000);
916                 }
917
918                 // Return result
919                 return $hasSlept;
920         }
921
922         /**
923          * Checks whether the given encoded data was encoded with Base64
924          *
925          * @param       $encodedData    Encoded data we shall check
926          * @return      $isBase64               Whether the encoded data is Base64
927          * @throws      InvalidArgumentException        If a parameter is not valid
928          */
929         protected function isBase64Encoded (string $encodedData) {
930                 // Check parameter
931                 if (empty($encodedData)) {
932                         // Throw IAE
933                         throw new InvalidArgumentException('Parameter "encodedData" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
934                 }
935
936                 // Determine it
937                 $isBase64 = (@base64_decode($encodedData, true) !== false);
938
939                 // Return it
940                 return $isBase64;
941         }
942
943         /**
944          * Getter for startup time in miliseconds
945          *
946          * @return      $startupTime    Startup time in miliseconds
947          */
948         protected function getStartupTime () {
949                 return self::$startupTime;
950         }
951
952         /**
953          * "Getter" for a printable currently execution time in nice braces
954          *
955          * @return      $executionTime  Current execution time in nice braces
956          */
957         protected function getPrintableExecutionTime () {
958                 // Calculate execution time and pack it in nice braces
959                 $executionTime = sprintf('[ %01.5f ] ', (microtime(true) - $this->getStartupTime()));
960
961                 // And return it
962                 return $executionTime;
963         }
964
965         /**
966          * Determines if an element is set in the generic array
967          *
968          * @param       $keyGroup       Main group for the key
969          * @param       $subGroup       Sub group for the key
970          * @param       $key            Key to check
971          * @param       $element        Element to check
972          * @return      $isset          Whether the given key is set
973          * @throws      InvalidArgumentException        If a parameter is not valid
974          */
975         protected final function isGenericArrayElementSet (string $keyGroup, string $subGroup, string $key, string $element) {
976                 // Check parameter
977                 if (empty($keyGroup)) {
978                         // Throw IAE
979                         throw new InvalidArgumentException('Parameter "keyGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
980                 } elseif (empty($subGroup)) {
981                         // Throw IAE
982                         throw new InvalidArgumentException('Parameter "subGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
983                 } elseif (empty($key)) {
984                         // Throw IAE
985                         throw new InvalidArgumentException('Parameter "key" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
986                 } elseif ($element === '') {
987                         // Throw IAE
988                         throw new InvalidArgumentException('Parameter "element" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
989                 }
990
991                 // Is it there?
992                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',element=' . $element);
993                 $isset = isset($this->genericArray[$keyGroup][$subGroup][$key][$element]);
994
995                 // Return it
996                 return $isset;
997         }
998         /**
999          * Determines if a key is set in the generic array
1000          *
1001          * @param       $keyGroup       Main group for the key
1002          * @param       $subGroup       Sub group for the key
1003          * @param       $key            Key to check
1004          * @return      $isset          Whether the given key is set
1005          * @throws      InvalidArgumentException        If a parameter is not valid
1006          */
1007         protected final function isGenericArrayKeySet (string $keyGroup, string $subGroup, string $key) {
1008                 // Check parameter
1009                 if (empty($keyGroup)) {
1010                         // Throw IAE
1011                         throw new InvalidArgumentException('Parameter "keyGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1012                 } elseif (empty($subGroup)) {
1013                         // Throw IAE
1014                         throw new InvalidArgumentException('Parameter "subGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1015                 } elseif (empty($key)) {
1016                         // Throw IAE
1017                         throw new InvalidArgumentException('Parameter "key" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1018                 }
1019
1020                 // Is it there?
1021                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key);
1022                 $isset = isset($this->genericArray[$keyGroup][$subGroup][$key]);
1023
1024                 // Return it
1025                 return $isset;
1026         }
1027
1028         /**
1029          * Determines if a group is set in the generic array
1030          *
1031          * @param       $keyGroup       Main group
1032          * @param       $subGroup       Sub group
1033          * @return      $isset          Whether the given group is set
1034          * @throws      InvalidArgumentException        If a parameter is not valid
1035          */
1036         protected final function isGenericArrayGroupSet (string $keyGroup, string $subGroup) {
1037                 // Check parameter
1038                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup);
1039                 if (empty($keyGroup)) {
1040                         // Throw IAE
1041                         throw new InvalidArgumentException('Parameter "keyGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1042                 } elseif (empty($subGroup)) {
1043                         // Throw IAE
1044                         throw new InvalidArgumentException('Parameter "subGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1045                 }
1046
1047                 // Is it there?
1048                 $isset = isset($this->genericArray[$keyGroup][$subGroup]);
1049
1050                 // Return it
1051                 return $isset;
1052         }
1053
1054         /**
1055          * Getter for sub key group
1056          *
1057          * @param       $keyGroup       Main key group
1058          * @param       $subGroup       Sub key group
1059          * @return      $array          An array with all array elements
1060          * @throws      InvalidArgumentException        If a parameter is not valid
1061          * @throws      BadMethodCallException  If key/sub group isn't there but this method is invoked
1062          */
1063         protected final function getGenericSubArray (string $keyGroup, string $subGroup) {
1064                 // Check parameter
1065                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',value=' . print_r($this->genericArray[$keyGroup][$subGroup], true));
1066                 if (empty($keyGroup)) {
1067                         // Throw IAE
1068                         throw new InvalidArgumentException('Parameter "keyGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1069                 } elseif (empty($subGroup)) {
1070                         // Throw IAE
1071                         throw new InvalidArgumentException('Parameter "subGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1072                 } elseif (!$this->isGenericArrayGroupSet($keyGroup, $subGroup)) {
1073                         // No, then abort here
1074                         throw new BadMethodCallException(sprintf('keyGroup=%s,subGroup=%s not found.', $keyGroup, $subGroup), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL);
1075                 }
1076
1077                 // Return it
1078                 return $this->genericArray[$keyGroup][$subGroup];
1079         }
1080
1081         /**
1082          * Unsets a given key in generic array
1083          *
1084          * @param       $keyGroup       Main group for the key
1085          * @param       $subGroup       Sub group for the key
1086          * @param       $key            Key to unset
1087          * @return      void
1088          * @throws      InvalidArgumentException        If a parameter is not valid
1089          */
1090         protected final function unsetGenericArrayKey (string $keyGroup, string $subGroup, string $key) {
1091                 // Check parameter
1092                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key);
1093                 if (empty($keyGroup)) {
1094                         // Throw IAE
1095                         throw new InvalidArgumentException('Parameter "keyGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1096                 } elseif (empty($subGroup)) {
1097                         // Throw IAE
1098                         throw new InvalidArgumentException('Parameter "subGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1099                 } elseif (empty($key)) {
1100                         // Throw IAE
1101                         throw new InvalidArgumentException('Parameter "key" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1102                 }
1103
1104                 // Remove it
1105                 unset($this->genericArray[$keyGroup][$subGroup][$key]);
1106         }
1107
1108         /**
1109          * Unsets a given element in generic array
1110          *
1111          * @param       $keyGroup       Main group for the key
1112          * @param       $subGroup       Sub group for the key
1113          * @param       $key            Key to unset
1114          * @param       $element        Element to unset
1115          * @return      void
1116          * @throws      InvalidArgumentException        If a parameter is not valid
1117          */
1118         protected final function unsetGenericArrayElement (string $keyGroup, string $subGroup, string $key, string $element) {
1119                 // Check parameter
1120                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',element=' . $element);
1121                 if (empty($keyGroup)) {
1122                         // Throw IAE
1123                         throw new InvalidArgumentException('Parameter "keyGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1124                 } elseif (empty($subGroup)) {
1125                         // Throw IAE
1126                         throw new InvalidArgumentException('Parameter "subGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1127                 } elseif (empty($key)) {
1128                         // Throw IAE
1129                         throw new InvalidArgumentException('Parameter "key" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1130                 } elseif ($element === '') {
1131                         // Throw IAE
1132                         throw new InvalidArgumentException('Parameter "element" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1133                 }
1134
1135                 // Remove it
1136                 unset($this->genericArray[$keyGroup][$subGroup][$key][$element]);
1137         }
1138
1139         /**
1140          * Append a string to a given generic array key
1141          *
1142          * @param       $keyGroup       Main group for the key
1143          * @param       $subGroup       Sub group for the key
1144          * @param       $key            Key to unset
1145          * @param       $value          Value to add/append
1146          * @return      void
1147          * @throws      InvalidArgumentException        If a parameter is not valid
1148          */
1149         protected final function appendStringToGenericArrayKey (string $keyGroup, string $subGroup, $key, string $value, string $appendGlue = '') {
1150                 // Check parameter
1151                 //* NOISY-DEBUG: */ if (!is_object($value)) $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',value[' . gettype($value) . ']=' . print_r($value, true) . ',appendGlue=' . $appendGlue);
1152                 if (empty($keyGroup)) {
1153                         // Throw IAE
1154                         throw new InvalidArgumentException('Parameter "keyGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1155                 } elseif (empty($subGroup)) {
1156                         // Throw IAE
1157                         throw new InvalidArgumentException('Parameter "subGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1158                 } elseif (empty($key)) {
1159                         // Throw IAE
1160                         throw new InvalidArgumentException('Parameter "key" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1161                 }
1162
1163                 // Is it already there?
1164                 if ($this->isGenericArrayKeySet($keyGroup, $subGroup, $key)) {
1165                         // Append it
1166                         $this->genericArray[$keyGroup][$subGroup][$key] .= $appendGlue . $value;
1167                 } else {
1168                         // Add it
1169                         $this->genericArray[$keyGroup][$subGroup][$key] = $value;
1170                 }
1171         }
1172
1173         /**
1174          * Append a string to a given generic array element
1175          *
1176          * @param       $keyGroup       Main group for the key
1177          * @param       $subGroup       Sub group for the key
1178          * @param       $key            Key to unset
1179          * @param       $element        Element to check
1180          * @param       $value          Value to add/append
1181          * @return      void
1182          * @throws      InvalidArgumentException        If a parameter is not valid
1183          */
1184         protected final function appendStringToGenericArrayElement (string $keyGroup, string $subGroup, string $key, string $element, string $value, string $appendGlue = '') {
1185                 // Check parameter
1186                 //* NOISY-DEBUG: */ if (!is_object($value)) $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',element=' . $element . ',value[' . gettype($value) . ']=' . print_r($value, true) . ',appendGlue=' . $appendGlue);
1187                 if (empty($keyGroup)) {
1188                         // Throw IAE
1189                         throw new InvalidArgumentException('Parameter "keyGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1190                 } elseif (empty($subGroup)) {
1191                         // Throw IAE
1192                         throw new InvalidArgumentException('Parameter "subGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1193                 } elseif (empty($key)) {
1194                         // Throw IAE
1195                         throw new InvalidArgumentException('Parameter "key" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1196                 } elseif ($element === '') {
1197                         // Throw IAE
1198                         throw new InvalidArgumentException('Parameter "element" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1199                 }
1200
1201                 // Is it already there?
1202                 if ($this->isGenericArrayElementSet($keyGroup, $subGroup, $key, $element)) {
1203                         // Append it
1204                         $this->genericArray[$keyGroup][$subGroup][$key][$element] .= $appendGlue . $value;
1205                 } else {
1206                         // Add it
1207                         $this->setGenericArrayElement($keyGroup, $subGroup, $key, $element, $value);
1208                 }
1209         }
1210
1211         /**
1212          * Initializes given generic array group
1213          *
1214          * @param       $keyGroup       Main group for the key
1215          * @param       $subGroup       Sub group for the key
1216          * @param       $key            Key to use
1217          * @param       $forceInit      Optionally force initialization
1218          * @return      void
1219          * @throws      InvalidArgumentException        If a parameter is not valid
1220          * @throws      BadMethodCallException  If key/sub group has already been initialized
1221          */
1222         protected final function initGenericArrayGroup (string $keyGroup, string $subGroup, bool $forceInit = false) {
1223                 // Check parameter
1224                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',forceInit=' . intval($forceInit));
1225                 if (empty($keyGroup)) {
1226                         // Throw IAE
1227                         throw new InvalidArgumentException('Parameter "keyGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1228                 } elseif (empty($subGroup)) {
1229                         // Throw IAE
1230                         throw new InvalidArgumentException('Parameter "subGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1231                 } elseif (($forceInit === false) && ($this->isGenericArrayGroupSet($keyGroup, $subGroup))) {
1232                         // Already initialized
1233                         throw new BadMethodCallException(sprintf('keyGroup=%s,subGroup=%s already initialized.', $keyGroup, $subGroup), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL);
1234                 }
1235
1236                 // Initialize it
1237                 $this->genericArray[$keyGroup][$subGroup] = [];
1238         }
1239
1240         /**
1241          * Initializes given generic array key
1242          *
1243          * @param       $keyGroup       Main group for the key
1244          * @param       $subGroup       Sub group for the key
1245          * @param       $key            Key to use
1246          * @param       $forceInit      Optionally force initialization
1247          * @return      void
1248          * @throws      InvalidArgumentException        If a parameter is not valid
1249          * @throws      BadMethodCallException  If key/sub group has already been initialized
1250          */
1251         protected final function initGenericArrayKey (string $keyGroup, string $subGroup, $key, bool $forceInit = false) {
1252                 // Check parameter
1253                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',forceInit=' . intval($forceInit));
1254                 if (empty($keyGroup)) {
1255                         // Throw IAE
1256                         throw new InvalidArgumentException('Parameter "keyGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1257                 } elseif (empty($subGroup)) {
1258                         // Throw IAE
1259                         throw new InvalidArgumentException('Parameter "subGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1260                 } elseif (empty($key)) {
1261                         // Throw IAE
1262                         throw new InvalidArgumentException('Parameter "key" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1263                 } elseif (($forceInit === false) && ($this->isGenericArrayKeySet($keyGroup, $subGroup, $key))) {
1264                         // Already initialized
1265                         throw new BadMethodCallException(sprintf('keyGroup=%s,subGroup=%s,key[%s]=%s already initialized.', $keyGroup, $subGroup, gettype($key), $key), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL);
1266                 }
1267
1268                 // Initialize it
1269                 $this->genericArray[$keyGroup][$subGroup][$key] = [];
1270         }
1271
1272         /**
1273          * Initializes given generic array element
1274          *
1275          * @param       $keyGroup       Main group for the key
1276          * @param       $subGroup       Sub group for the key
1277          * @param       $key            Key to use
1278          * @param       $element        Element to use
1279          * @param       $forceInit      Optionally force initialization
1280          * @return      void
1281          * @throws      InvalidArgumentException        If a parameter is not valid
1282          * @throws      BadMethodCallException  If key/sub group isn't there but this method is invoked
1283          */
1284         protected final function initGenericArrayElement (string $keyGroup, string $subGroup, string $key, string $element, bool $forceInit = false) {
1285                 // Check parameter
1286                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',element=' . $element . ',forceInit=' . intval($forceInit));
1287                 if (empty($keyGroup)) {
1288                         // Throw IAE
1289                         throw new InvalidArgumentException('Parameter "keyGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1290                 } elseif (empty($subGroup)) {
1291                         // Throw IAE
1292                         throw new InvalidArgumentException('Parameter "subGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1293                 } elseif (empty($key)) {
1294                         // Throw IAE
1295                         throw new InvalidArgumentException('Parameter "key" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1296                 } elseif ($element === '') {
1297                         // Throw IAE
1298                         throw new InvalidArgumentException('Parameter "element" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1299                 } elseif (($forceInit === false) && ($this->isGenericArrayElementSet($keyGroup, $subGroup, $key, $element))) {
1300                         // Already initialized
1301                         throw new BadMethodCallException(sprintf('keyGroup=%s,subGroup=%s,key[%s]=%s,element[%s]=%s already initialized.', $keyGroup, $subGroup, gettype($key), $key, gettype($element), $element), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL);
1302                 }
1303
1304                 // Initialize it
1305                 $this->genericArray[$keyGroup][$subGroup][$key][$element] = [];
1306         }
1307
1308         /**
1309          * Pushes an element to a generic key. If the key isn't found, it will be initialized.
1310          *
1311          * @param       $keyGroup       Main group for the key
1312          * @param       $subGroup       Sub group for the key
1313          * @param       $key            Key to use
1314          * @param       $value          Value to add/append
1315          * @return      $count          Number of array elements
1316          * @throws      InvalidArgumentException        If a parameter is not valid
1317          */
1318         protected final function pushValueToGenericArrayKey (string $keyGroup, string $subGroup, string $key, $value) {
1319                 // Check parameter
1320                 //* NOISY-DEBUG: */ if (!is_object($value)) $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',value[' . gettype($value) . ']=' . print_r($value, true));
1321                 if (empty($keyGroup)) {
1322                         // Throw IAE
1323                         throw new InvalidArgumentException('Parameter "keyGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1324                 } elseif (empty($subGroup)) {
1325                         // Throw IAE
1326                         throw new InvalidArgumentException('Parameter "subGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1327                 } elseif (empty($key)) {
1328                         // Throw IAE
1329                         throw new InvalidArgumentException('Parameter "key" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1330                 } elseif (!$this->isGenericArrayKeySet($keyGroup, $subGroup, $key)) {
1331                         // Initialize array
1332                         $this->initGenericArrayKey($keyGroup, $subGroup, $key);
1333                 }
1334
1335                 // Then push it
1336                 $count = array_push($this->genericArray[$keyGroup][$subGroup][$key], $value);
1337
1338                 // Return count
1339                 //* DEBUG: */ print(__METHOD__ . ': genericArray=' . print_r($this->genericArray[$keyGroup][$subGroup][$key], true));
1340                 //* DEBUG: */ print(__METHOD__ . ': count=' . $count . PHP_EOL);
1341                 return $count;
1342         }
1343
1344         /**
1345          * Pushes an element to a generic array element. If the key isn't found, it will be initialized.
1346          *
1347          * @param       $keyGroup       Main group for the key
1348          * @param       $subGroup       Sub group for the key
1349          * @param       $key            Key to use
1350          * @param       $element        Element to check
1351          * @param       $value          Value to add/append
1352          * @return      $count          Number of array elements
1353          * @throws      InvalidArgumentException        If a parameter is not valid
1354          */
1355         protected final function pushValueToGenericArrayElement (string $keyGroup, string $subGroup, string $key, string $element, $value) {
1356                 // Check parameter
1357                 //* NOISY-DEBUG: */ if (!is_object($value)) $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',element=' . $element . ',value[' . gettype($value) . ']=' . print_r($value, true));
1358                 if (empty($keyGroup)) {
1359                         // Throw IAE
1360                         throw new InvalidArgumentException('Parameter "keyGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1361                 } elseif (empty($subGroup)) {
1362                         // Throw IAE
1363                         throw new InvalidArgumentException('Parameter "subGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1364                 } elseif (empty($key)) {
1365                         // Throw IAE
1366                         throw new InvalidArgumentException('Parameter "key" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1367                 } elseif ($element === '') {
1368                         // Throw IAE
1369                         throw new InvalidArgumentException('Parameter "element" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1370                 } elseif (!$this->isGenericArrayElementSet($keyGroup, $subGroup, $key, $element)) {
1371                         // Initialize array
1372                         $this->initGenericArrayElement($keyGroup, $subGroup, $key, $element);
1373                 }
1374
1375                 // Then push it
1376                 $count = array_push($this->genericArray[$keyGroup][$subGroup][$key][$element], $value);
1377
1378                 // Return count
1379                 //* DEBUG: */ print(__METHOD__ . ': genericArray=' . print_r($this->genericArray[$keyGroup][$subGroup][$key], true));
1380                 //* DEBUG: */ print(__METHOD__ . ': count=' . $count . PHP_EOL);
1381                 return $count;
1382         }
1383
1384         /**
1385          * Pops an element from  a generic group
1386          *
1387          * @param       $keyGroup       Main group for the key
1388          * @param       $subGroup       Sub group for the key
1389          * @param       $key            Key to unset
1390          * @return      $value          Last "popped" value
1391          * @throws      InvalidArgumentException        If a parameter is not valid
1392          * @throws      BadMethodCallException  If key/sub group isn't there but this method is invoked
1393          */
1394         protected final function popGenericArrayElement (string $keyGroup, string $subGroup, string $key) {
1395                 // Check parameter
1396                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key);
1397                 if (empty($keyGroup)) {
1398                         // Throw IAE
1399                         throw new InvalidArgumentException('Parameter "keyGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1400                 } elseif (empty($subGroup)) {
1401                         // Throw IAE
1402                         throw new InvalidArgumentException('Parameter "subGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1403                 } elseif (empty($key)) {
1404                         // Throw IAE
1405                         throw new InvalidArgumentException('Parameter "key" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1406                 } elseif (!$this->isGenericArrayKeySet($keyGroup, $subGroup, $key)) {
1407                         // Not found
1408                         throw new BadMethodCallException(sprintf('keyGroup=%s,subGroup=%s,key[%s]=%s not found.', $keyGroup, $subGroup, gettype($key), $key), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL);
1409                 }
1410
1411                 // Then "pop" it
1412                 $value = array_pop($this->genericArray[$keyGroup][$subGroup][$key]);
1413
1414                 // Return value
1415                 //* DEBUG: */ print(__METHOD__ . ': genericArray=' . print_r($this->genericArray[$keyGroup][$subGroup][$key], true));
1416                 //* DEBUG: */ print(__METHOD__ . ': value[' . gettype($value) . ']=' . print_r($value, true) . PHP_EOL);
1417                 return $value;
1418         }
1419
1420         /**
1421          * Shifts an element from  a generic group
1422          *
1423          * @param       $keyGroup       Main group for the key
1424          * @param       $subGroup       Sub group for the key
1425          * @param       $key            Key to unset
1426          * @return      $value          Last "shifted" value
1427          * @throws      InvalidArgumentException        If a parameter is not valid
1428          * @throws      BadMethodCallException  If key/sub group isn't there but this method is invoked
1429          */
1430         protected final function shiftGenericArrayElement (string $keyGroup, string $subGroup, string $key) {
1431                 // Check parameter
1432                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key);
1433                 if (empty($keyGroup)) {
1434                         // Throw IAE
1435                         throw new InvalidArgumentException('Parameter "keyGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1436                 } elseif (empty($subGroup)) {
1437                         // Throw IAE
1438                         throw new InvalidArgumentException('Parameter "subGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1439                 } elseif (empty($key)) {
1440                         // Throw IAE
1441                         throw new InvalidArgumentException('Parameter "key" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1442                 } elseif (!$this->isGenericArrayKeySet($keyGroup, $subGroup, $key)) {
1443                         // Not found
1444                         throw new BadMethodCallException(sprintf('keyGroup=%s,subGroup=%s,key[%s]=%s not found.', $keyGroup, $subGroup, gettype($key), $key), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL);
1445                 }
1446
1447                 // Then "shift" it
1448                 $value = array_shift($this->genericArray[$keyGroup][$subGroup][$key]);
1449
1450                 // Return value
1451                 //* DEBUG: */ print(__METHOD__ . ': genericArray=' . print_r($this->genericArray[$keyGroup][$subGroup][$key], true));
1452                 //* DEBUG: */ print(__METHOD__ . ': value[' . gettype($value) . ']=' . print_r($value, true) . PHP_EOL);
1453                 return $value;
1454         }
1455
1456         /**
1457          * Count generic array group
1458          *
1459          * @param       $keyGroup       Main group for the key
1460          * @return      $count          Count of given group
1461          * @throws      InvalidArgumentException        If a parameter is not valid
1462          * @throws      BadMethodCallException  If key group isn't there but this method is invoked
1463          */
1464         protected final function countGenericArray (string $keyGroup) {
1465                 // Check parameter
1466                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup);
1467                 if (empty($keyGroup)) {
1468                         // Throw IAE
1469                         throw new InvalidArgumentException('Parameter "keyGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1470                 } elseif (!isset($this->genericArray[$keyGroup])) {
1471                         // Not found
1472                         throw new BadMethodCallException(sprintf('keyGroup=%s not found.', $keyGroup), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL);
1473                 }
1474
1475                 // Then count it
1476                 $count = count($this->genericArray[$keyGroup]);
1477
1478                 // Return it
1479                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',count=' . $count);
1480                 return $count;
1481         }
1482
1483         /**
1484          * Count generic array sub group
1485          *
1486          * @param       $keyGroup       Main group for the key
1487          * @param       $subGroup       Sub group for the key
1488          * @return      $count          Count of given group
1489          * @throws      InvalidArgumentException        If a parameter is not valid
1490          * @throws      BadMethodCallException  If key/sub group isn't there but this method is invoked
1491          */
1492         protected final function countGenericArrayGroup (string $keyGroup, string $subGroup) {
1493                 // Check parameter
1494                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup);
1495                 if (empty($keyGroup)) {
1496                         // Throw IAE
1497                         throw new InvalidArgumentException('Parameter "keyGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1498                 } elseif (empty($subGroup)) {
1499                         // Throw IAE
1500                         throw new InvalidArgumentException('Parameter "subGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1501                 } elseif (!$this->isGenericArrayGroupSet($keyGroup, $subGroup)) {
1502                         // Abort here
1503                         throw new BadMethodCallException(sprintf('keyGroup=%s,subGroup=%s not found.', $keyGroup, $subGroup), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL);
1504                 }
1505
1506                 // Then count it
1507                 $count = count($this->genericArray[$keyGroup][$subGroup]);
1508
1509                 // Return it
1510                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',count=' . $count);
1511                 return $count;
1512         }
1513
1514         /**
1515          * Count generic array elements
1516          *
1517          * @param       $keyGroup       Main group for the key
1518          * @param       $subGroup       Sub group for the key
1519          * @param       $key            Key to count
1520          * @return      $count          Count of given key
1521          * @throws      InvalidArgumentException        If a parameter is not valid
1522          * @throws      BadMethodCallException  If key/sub group isn't there but this method is invoked
1523          */
1524         protected final function countGenericArrayElements (string $keyGroup, string $subGroup, string $key) {
1525                 // Check parameter
1526                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key);
1527                 if (empty($keyGroup)) {
1528                         // Throw IAE
1529                         throw new InvalidArgumentException('Parameter "keyGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1530                 } elseif (empty($subGroup)) {
1531                         // Throw IAE
1532                         throw new InvalidArgumentException('Parameter "subGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1533                 } elseif (empty($key)) {
1534                         // Throw IAE
1535                         throw new InvalidArgumentException('Parameter "key" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1536                 } elseif (!$this->isGenericArrayKeySet($keyGroup, $subGroup, $key)) {
1537                         // Abort here
1538                         throw new BadMethodCallException(sprintf('keyGroup=%s,subGroup=%s,key[%s]=%s not found.', $keyGroup, $subGroup, gettype($key), $key), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL);
1539                 } elseif (!$this->isValidGenericArrayGroup($keyGroup, $subGroup)) {
1540                         // Not valid
1541                         throw new BadMethodCallException(sprintf('keyGroup=%s,subGroup=%s is not a valid key/sub group.', $keyGroup, $subGroup), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL);
1542                 }
1543
1544                 // Then count it
1545                 $count = count($this->genericArray[$keyGroup][$subGroup][$key]);
1546
1547                 // Return it
1548                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',count=' . $count);
1549                 return $count;
1550         }
1551
1552         /**
1553          * Getter for whole generic group array
1554          *
1555          * @param       $keyGroup       Key group to get
1556          * @return      $array          Whole generic array group
1557          * @throws      InvalidArgumentException        If a parameter is not valid
1558          * @throws      BadMethodCallException  If key/sub group isn't there but this method is invoked
1559          */
1560         protected final function getGenericArray (string $keyGroup) {
1561                 // Check parameters
1562                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup);
1563                 if (empty($keyGroup)) {
1564                         // Throw IAE
1565                         throw new InvalidArgumentException('Parameter "keyGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1566                 } elseif (!isset($this->genericArray[$keyGroup])) {
1567                         // Then abort here
1568                         throw new BadMethodCallException(sprintf('keyGroup=%s not found', $keyGroup), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL);
1569                 }
1570
1571                 // Return it
1572                 return $this->genericArray[$keyGroup];
1573         }
1574
1575         /**
1576          * Setter for generic array key
1577          *
1578          * @param       $keyGroup       Key group to get
1579          * @param       $subGroup       Sub group for the key
1580          * @param       $key            Key to unset
1581          * @param       $value          Mixed value from generic array element
1582          * @return      void
1583          * @throws      InvalidArgumentException        If a parameter is not valid
1584          * @throws      BadMethodCallException  If key/sub group isn't there but this method is invoked
1585          */
1586         protected final function setGenericArrayKey (string $keyGroup, string $subGroup, string $key, $value) {
1587                 // Check parameters
1588                 //* NOISY-DEBUG: */ if (!is_object($value)) $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',value[' . gettype($value) . ']=' . print_r($value, true));
1589                 if (empty($keyGroup)) {
1590                         // Throw IAE
1591                         throw new InvalidArgumentException('Parameter "keyGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1592                 } elseif (empty($subGroup)) {
1593                         // Throw IAE
1594                         throw new InvalidArgumentException('Parameter "subGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1595                 } elseif (empty($key)) {
1596                         // Throw IAE
1597                         throw new InvalidArgumentException('Parameter "key" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1598                 } elseif (!$this->isValidGenericArrayGroup($keyGroup, $subGroup)) {
1599                         // Then abort here
1600                         throw new BadMethodCallException(sprintf('keyGroup=%s,subGroup=%s not found', $keyGroup), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL);
1601                 }
1602
1603                 // Set value here
1604                 $this->genericArray[$keyGroup][$subGroup][$key] = $value;
1605         }
1606
1607         /**
1608          * Getter for generic array key
1609          *
1610          * @param       $keyGroup       Key group to get
1611          * @param       $subGroup       Sub group for the key
1612          * @param       $key            Key to unset
1613          * @return      $value          Mixed value from generic array element
1614          * @throws      InvalidArgumentException        If a parameter is not valid
1615          * @throws      BadMethodCallException  If key/sub group isn't there but this method is invoked
1616          */
1617         protected final function getGenericArrayKey (string $keyGroup, string $subGroup, string $key) {
1618                 // Check parameters
1619                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key);
1620                 if (empty($keyGroup)) {
1621                         // Throw IAE
1622                         throw new InvalidArgumentException('Parameter "keyGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1623                 } elseif (empty($subGroup)) {
1624                         // Throw IAE
1625                         throw new InvalidArgumentException('Parameter "subGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1626                 } elseif (empty($key)) {
1627                         // Throw IAE
1628                         throw new InvalidArgumentException('Parameter "key" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1629                 } elseif (!$this->isGenericArrayKeySet($keyGroup, $subGroup, $key)) {
1630                         // Then abort here
1631                         throw new BadMethodCallException(sprintf('keyGroup=%s,subGroup=%s,key[%s]=%s not found.', $keyGroup, $subGroup, gettype($key), $key), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL);
1632                 }
1633
1634                 // Return it
1635                 return $this->genericArray[$keyGroup][$subGroup][$key];
1636         }
1637
1638         /**
1639          * Sets a value in given generic array key/element
1640          *
1641          * @param       $keyGroup       Main group for the key
1642          * @param       $subGroup       Sub group for the key
1643          * @param       $key            Key to set
1644          * @param       $element        Element to set
1645          * @param       $value          Value to set
1646          * @return      void
1647          * @throws      InvalidArgumentException        If a parameter is not valid
1648          */
1649         protected final function setGenericArrayElement (string $keyGroup, string $subGroup, string $key, string $element, $value) {
1650                 // Check parameter
1651                 //* NOISY-DEBUG: */ if (!is_object($value)) $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',element=' . $element . ',value[' . gettype($value) . ']=' . print_r($value, true));
1652                 if (empty($keyGroup)) {
1653                         // Throw IAE
1654                         throw new InvalidArgumentException('Parameter "keyGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1655                 } elseif (empty($subGroup)) {
1656                         // Throw IAE
1657                         throw new InvalidArgumentException('Parameter "subGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1658                 } elseif (empty($key)) {
1659                         // Throw IAE
1660                         throw new InvalidArgumentException('Parameter "key" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1661                 } elseif ($element === '') {
1662                         // Throw IAE
1663                         throw new InvalidArgumentException('Parameter "element" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1664                 } elseif (!$this->isGenericArrayElementSet($keyGroup, $subGroup, $key, $element)) {
1665                         // Initialize array
1666                         $this->initGenericArrayElement($keyGroup, $subGroup, $key, $element);
1667                 }
1668
1669                 // Then set it
1670                 $this->genericArray[$keyGroup][$subGroup][$key][$element] = $value;
1671         }
1672
1673         /**
1674          * Getter for generic array element
1675          *
1676          * @param       $keyGroup       Key group to get
1677          * @param       $subGroup       Sub group for the key
1678          * @param       $key            Key to look for
1679          * @param       $element        Element to look for
1680          * @return      $value          Mixed value from generic array element
1681          * @throws      InvalidArgumentException        If a parameter is not valid
1682          * @throws      BadMethodCallException  If key/sub group isn't there but this method is invoked
1683          */
1684         protected final function getGenericArrayElement (string $keyGroup, string $subGroup, string $key, string $element) {
1685                 // Check parameter
1686                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key . ',element=' . $element);
1687                 if (empty($keyGroup)) {
1688                         // Throw IAE
1689                         throw new InvalidArgumentException('Parameter "keyGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1690                 } elseif (empty($subGroup)) {
1691                         // Throw IAE
1692                         throw new InvalidArgumentException('Parameter "subGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1693                 } elseif (empty($key)) {
1694                         // Throw IAE
1695                         throw new InvalidArgumentException('Parameter "key" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1696                 } elseif ($element === '') {
1697                         // Throw IAE
1698                         throw new InvalidArgumentException('Parameter "element" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1699                 } elseif (!$this->isGenericArrayElementSet($keyGroup, $subGroup, $key, $element)) {
1700                         // Then abort here
1701                         throw new BadMethodCallException(sprintf('keyGroup=%s,subGroup=%s,key[%s]=%s,element[%s]=%s not found.', $keyGroup, $subGroup, gettype($key), $key, gettype($element), $element), FrameworkInterface::EXCEPTION_BAD_METHOD_CALL);
1702                 }
1703
1704                 // Return it
1705                 return $this->genericArray[$keyGroup][$subGroup][$key][$element];
1706         }
1707
1708         /**
1709          * Checks if a given sub group is valid (array)
1710          *
1711          * @param       $keyGroup       Key group to get
1712          * @param       $subGroup       Sub group for the key
1713          * @return      $isValid        Whether given sub group is valid
1714          * @throws      InvalidArgumentException        If a parameter is not valid
1715          */
1716         protected final function isValidGenericArrayGroup (string $keyGroup, string $subGroup) {
1717                 // Check parameter
1718                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup);
1719                 if (empty($keyGroup)) {
1720                         // Throw IAE
1721                         throw new InvalidArgumentException('Parameter "keyGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1722                 } elseif (empty($subGroup)) {
1723                         // Throw IAE
1724                         throw new InvalidArgumentException('Parameter "subGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1725                 }
1726
1727                 // Determine it
1728                 $isValid = (($this->isGenericArrayGroupSet($keyGroup, $subGroup)) && (is_array($this->getGenericSubArray($keyGroup, $subGroup))));
1729
1730                 // Return it
1731                 return $isValid;
1732         }
1733
1734         /**
1735          * Checks if a given key is valid (array)
1736          *
1737          * @param       $keyGroup       Key group to get
1738          * @param       $subGroup       Sub group for the key
1739          * @param       $key            Key to check
1740          * @return      $isValid        Whether given sub group is valid
1741          */
1742         protected final function isValidGenericArrayKey (string $keyGroup, string $subGroup, string $key) {
1743                 // Check parameters
1744                 //* NOISY-DEBUG: */ $this->outputLine('[' . __METHOD__ . ':' . __LINE__ . '] keyGroup=' . $keyGroup . ',subGroup=' . $subGroup . ',key=' . $key);
1745                 if (empty($keyGroup)) {
1746                         // Throw IAE
1747                         throw new InvalidArgumentException('Parameter "keyGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1748                 } elseif (empty($subGroup)) {
1749                         // Throw IAE
1750                         throw new InvalidArgumentException('Parameter "subGroup" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1751                 } elseif (empty($key)) {
1752                         // Throw IAE
1753                         throw new InvalidArgumentException('Parameter "key" is empty', FrameworkInterface::EXCEPTION_INVALID_ARGUMENT);
1754                 }
1755
1756                 // Determine it
1757                 $isValid = (($this->isGenericArrayKeySet($keyGroup, $subGroup, $key)) && (is_array($this->getGenericArrayKey($keyGroup, $subGroup, $key))));
1758
1759                 // Return it
1760                 return $isValid;
1761         }
1762
1763         /**
1764          * Initializes the web output instance
1765          *
1766          * @return      void
1767          */
1768         protected function initWebOutputInstance () {
1769                 // Init web output instance
1770                 $outputInstance = ObjectFactory::createObjectByConfiguredName('output_class');
1771
1772                 // Set it locally
1773                 $this->setWebOutputInstance($outputInstance);
1774         }
1775
1776         /**
1777          * Translates boolean true to 'Y' and false to 'N'
1778          *
1779          * @param       $boolean                Boolean value
1780          * @return      $translated             Translated boolean value
1781          */
1782         public static final function translateBooleanToYesNo (bool $boolean) {
1783                 // "Translate" it
1784                 $translated = ($boolean === true) ? 'Y' : 'N';
1785
1786                 // ... and return it
1787                 return $translated;
1788         }
1789
1790         /**
1791          * Creates a full-qualified file name (FQFN) for given file name by adding
1792          * a configured temporary file path to it.
1793          *
1794          * @param       $infoInstance   An instance of a SplFileInfo class
1795          * @return      $tempInstance   An instance of a SplFileInfo class (temporary file)
1796          * @throw       PathWriteProtectedException If the path in 'temp_file_path' is write-protected
1797          * @throws      FileIoException If the file cannot be written
1798          */
1799          protected static function createTempPathForFile (SplFileInfo $infoInstance) {
1800                 // Get config entry
1801                 $basePath = FrameworkBootstrap::getConfigurationInstance()->getConfigEntry('temp_file_path');
1802
1803                 // Is the path writeable?
1804                 if (!is_writable($basePath)) {
1805                         // Path is write-protected
1806                         throw new PathWriteProtectedException($infoInstance, self::EXCEPTION_PATH_CANNOT_BE_WRITTEN);
1807                 }
1808
1809                 // Add it
1810                 $tempInstance = new SplFileInfo($basePath . DIRECTORY_SEPARATOR . $infoInstance->getBasename());
1811
1812                 // Is it reachable?
1813                 if (!FrameworkBootstrap::isReachableFilePath($tempInstance)) {
1814                         // Not reachable
1815                         throw new FileIoException($tempInstance, self::EXCEPTION_FILE_NOT_REACHABLE);
1816                 }
1817
1818                 // Return it
1819                 return $tempInstance;
1820          }
1821
1822         /**
1823          * "Getter" for a printable state name
1824          *
1825          * @return      $stateName      Name of the node's state in a printable format
1826          * @todo        Move this class away from this monolithic place (not whole class is monolithic)
1827          */
1828         public final function getPrintableState () {
1829                 // Default is 'null'
1830                 $stateName = 'null';
1831
1832                 // Get the state instance
1833                 $stateInstance = $this->getStateInstance();
1834
1835                 // Is it an instance of Stateable?
1836                 if ($stateInstance instanceof Stateable) {
1837                         // Then use that state name
1838                         $stateName = $stateInstance->getStateName();
1839                 }
1840
1841                 // Return result
1842                 return $stateName;
1843         }
1844
1845 }