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