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