Throwing an NPE here hides the actual exception and it is much harder to track what...
[core.git] / inc / classes / main / helper / class_BaseHelper.php
1 <?php
2 /**
3  * A generic helper class with generic methods
4  *
5  * @author              Roland Haeder <webmaster@shipsimu.org>
6  * @version             0.0.0
7  * @copyright   Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2015 Core Developer Team
8  * @license             GNU GPL 3.0 or any newer version
9  * @link                http://www.shipsimu.org
10  *
11  * This program is free software: you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation, either version 3 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program. If not, see <http://www.gnu.org/licenses/>.
23  */
24 class BaseHelper extends BaseFrameworkSystem {
25         /**
26          * Instance to the class which provides field values
27          */
28         private $valueInstance = NULL;
29
30         /**
31          * Extra instance to the class which provides field values
32          */
33         private $extraInstance = NULL;
34
35         /**
36          * Rendered content created by the helper class
37          */
38         private $content = '';
39
40         /**
41          * Array with groups
42          */
43         private $groups = array();
44
45         /**
46          * Array with sub group
47          */
48         private $subGroups = array();
49
50         /**
51          * Previously opened group
52          */
53         private $previousGroupId = '';
54
55         /**
56          * Previously opened sub group
57          */
58         private $previousSubGroupId = '';
59
60         /**
61          * Total counter for groups and sub groups
62          */
63         private $totalCounter = 0;
64
65         // Exception constants
66         const EXCEPTION_GROUP_NOT_OPENED             = 0x1e3;
67         const EXCEPTION_GROUP_ALREADY_FOUND          = 0x1e4;
68         const EXCEPTION_SUB_GROUP_ALREADY_FOUND      = 0x1e5;
69         const EXCEPTION_NO_PREVIOUS_SUB_GROUP_OPENED = 0x1e6;
70         const EXCEPTION_NO_PREVIOUS_GROUP_OPENED     = 0x1e7;
71
72         /**
73          * Protected constructor
74          *
75          * @param       $className      Real name of the class
76          * @return      void
77          */
78         protected function __construct ($className) {
79                 // Call parent constructor
80                 parent::__construct($className);
81         }
82
83         /**
84          * Adds content directly
85          *
86          * @param       $newContent             New content to add
87          * @return      void
88          */
89         protected final function addContent ($newContent) {
90                 $this->content .= (string) trim($newContent) . PHP_EOL;
91         }
92
93         /**
94          * Add header content to the helper
95          *
96          * @param       $content        Content to to the base
97          * @return      void
98          */
99         protected function addHeaderContent ($content) {
100                 // Add the header content
101                 $this->groups['header']['content'] = (string) trim($content);
102         }
103
104         /**
105          * Add footer content to the helper
106          *
107          * @param       $content        Content to to the base
108          * @return      void
109          */
110         protected function addFooterContent ($content) {
111                 // Add the footer content
112                 $this->groups['footer']['content'] = (string) trim($content);
113         }
114
115         /**
116          * Adds content to the previously opened group or sub group. If a sub group
117          * was found it will be taken. If no group/sub group is opened at the moment
118          * the code will be passed to addContent().
119          *
120          * @param       $newContent             New content to add
121          * @return      void
122          */
123         protected final function addContentToPreviousGroup ($newContent) {
124                 // Check for sub/group
125                 if ($this->ifSubGroupOpenedPreviously()) {
126                         // Get sub group id
127                         $subGroupId = $this->getPreviousSubGroupId();
128
129                         // Add the content
130                         $this->subGroups[$subGroupId]['content'] .= $newContent;
131                 } elseif ($this->ifGroupOpenedPreviously()) {
132                         // Get group id
133                         $groupId = $this->getPreviousGroupId();
134
135                         // Add the content
136                         $this->groups[$groupId]['content'] .= $newContent;
137                 } else {
138                         // Add it directly
139                         $this->addContent($newContent);
140                 }
141         }
142
143         /**
144          * Getter for content
145          *
146          * @return      $content        The rendered content by this helper
147          */
148         protected final function getContent () {
149                 return $this->content;
150         }
151
152         /**
153          *  Assigns a field from the value instance with a template variable
154          *
155          * @param       $fieldName      Name of the field to assign
156          * @return      void
157          */
158         public function assignField ($fieldName) {
159                 // Get the value from value instance
160                 $fieldValue = $this->getValueField($fieldName);
161
162                 // Assign it with a template variable
163                 $this->getTemplateInstance()->assignVariable('block_' . $fieldName, $fieldValue);
164         }
165
166         /**
167          * Assigns a field from the value instance with a template variable but
168          * parses its content through a given filter method of the value instance
169          *
170          * @param       $fieldName              Name of the field to assign
171          * @param       $filterMethod   Method name to call of the value instance
172          * @return      void
173          * @todo        Rewrite this method using a helper class for filtering data
174          */
175         public function assignFieldWithFilter ($fieldName, $filterMethod) {
176                 // Get the value
177                 $fieldValue = $this->getValueField($fieldName);
178                 //* DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput($fieldName.'='.$fieldValue);
179
180                 // Now filter it through the value through the filter method
181                 $filteredValue = call_user_func_array(array($this, 'doFilter' . $this->convertToClassName($filterMethod)), array($fieldValue));
182
183                 // Assign it with a template variable
184                 $this->getTemplateInstance()->assignVariable('block_' . $fieldName, $filteredValue);
185         }
186
187         /**
188          * Pre-fetches field default values from the given registry key instance into this class
189          *
190          * @param       $registryKey    Registry key which holds an object with values
191          * @param       $extraKey               Extra value instance key used if registryKey is null
192          * @return      void
193          * @throws      NullPointerException    If recovery of requested value instance failed
194          */
195         public function prefetchValueInstance ($registryKey, $extraKey = NULL) {
196                 //* DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput('O:'.$registryKey.'/'.$extraKey);
197                 try {
198                         // Get the required instance
199                         $this->valueInstance = Registry::getRegistry()->getInstance($registryKey);
200                 } catch (NullPointerException $e) {
201                         // Not set in registry
202                         // @TODO Try to log it here
203                         //* DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput($registryKey.'=NULL!');
204                 }
205
206                 // Shall we get an extra instance?
207                 if (!is_null($extraKey)) {
208                         try {
209                                 // Get the extra instance.
210                                 $this->extraInstance = Registry::getRegistry()->getInstance($extraKey);
211                         } catch (NullPointerException $e) {
212                                 // Try to create it
213                                 $this->extraInstance = ObjectFactory::createObjectByConfiguredName($extraKey . '_class', array($this->valueInstance));
214                         }
215                         //* DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput($extraKey.'='.$this->extraInstance.' - EXTRA!');
216                 } // END - if
217
218                 // Is the value instance valid?
219                 if (is_null($this->valueInstance)) {
220                         // Get the requested instance
221                         $this->valueInstance = ObjectFactory::createObjectByConfiguredName($registryKey . '_class', array($this->extraInstance));
222                 } // END - if
223         }
224
225         /**
226          * Opens a helper group with given group id and content or throws an
227          * exception if that group is already found regardless if it is open or
228          * closed.
229          *
230          * @param       $groupId        Group id to open
231          * @param       $content        Initial content to add to the group
232          * @param       $tag            HTML tag used to open this group
233          * @return      void
234          * @throws      HelperGroupAlreadyCreatedException      If the group was already created before
235          */
236         protected function openGroupByIdContent ($groupId, $content, $tag) {
237                 //* DEBUG: */ echo "OPEN:groupId={$groupId},content=<pre>".htmlentities($content)."</pre>\n";
238                 // Is the group already there?
239                 if (isset($this->groups[$groupId])) {
240                         // Then throw an exception here
241                         throw new HelperGroupAlreadyCreatedException(array($this, $groupId), self::EXCEPTION_GROUP_ALREADY_FOUND);
242                 } // END - if
243
244                 // Count one up
245                 $this->totalCounter++;
246
247                 // Add the group to the stack
248                 $this->groups[$this->totalCounter] = $groupId;
249                 $this->groups[$groupId]['opened']  = TRUE;
250                 $this->groups[$groupId]['content'] = sprintf(
251                         '<!-- group %s opened (length: %s, tag: %s) //-->%s' . PHP_EOL,
252                         $groupId,
253                         strlen($content),
254                         $tag,
255                         $content
256                 );
257                 $this->groups[$groupId]['tag'] = $tag;
258
259                 // Mark this group as previously opened
260                 $this->setPreviousGroupId($groupId);
261         }
262
263         /**
264          * Closes the previously opened group by added given content to it or
265          * throws an exception if no previous group was opened
266          *
267          * @param       $content        Content for previously opened group, or empty to use tag of opener
268          * @return      void
269          * @throws      HelperNoPreviousOpenedGroupException    If no previously opened group was found
270          */
271         public function closePreviousGroupByContent ($content = '') {
272                 // Check if any sub group was opened before
273                 if ($this->ifSubGroupOpenedPreviously()) {
274                         // Close it automatically
275                         $this->closePreviousSubGroupByContent();
276                 } // END - if
277
278                 // Check if any group was opened before
279                 if ($this->ifGroupOpenedPreviously() === FALSE) {
280                         // Then throw an exception
281                         throw new HelperNoPreviousOpenedGroupException(array($this, $content), self::EXCEPTION_NO_PREVIOUS_SUB_GROUP_OPENED);
282                 } // END - if
283
284                 // Get previous group
285                 $groupId = $this->getPreviousGroupId();
286
287                 // Is the content empty?
288                 if ((empty($content)) && (!empty($this->groups[$groupId]['tag']))) {
289                         // Get it from opener
290                         $content = sprintf(
291                                 "<!-- group %s auto-closed //--></%s>",
292                                 $groupId,
293                                 $this->groups[$groupId]['tag']
294                         );
295                 } // END - if
296
297                 // Add content to it and mark it as closed
298                 $this->groups[$groupId]['content'] .= sprintf(
299                         "<!-- group %s closed (length: %s, tag: %s) //-->%s\n",
300                         $groupId,
301                         strlen($content),
302                         $this->groups[$groupId]['tag'],
303                         $content
304                 );
305                 $this->groups[$groupId]['opened'] = FALSE;
306
307                 // Mark previous group as closed
308                 $this->setPreviousGroupId('');
309                 //* DEBUG: */ echo "CLOSE:groupId={$groupId}<br />\n";
310         }
311
312         /**
313          * Opens a helper sub group with given group id and content or throws an
314          * exception if that sub group is already found regardless if it is open or
315          * closed.
316          *
317          * @param       $subGroupId             Sub group id to open
318          * @param       $content                Initial content to add to the sub group
319          * @param       $tag                    HTML tag used to open this group
320          * @return      void
321          * @throws      HelperSubGroupAlreadyCreatedException   If the sub group was already created before
322          */
323         protected function openSubGroupByIdContent ($subGroupId, $content, $tag) {
324                 //* DEBUG: */ echo "OPEN:subGroupId={$subGroupId},content=".htmlentities($content)."<br />\n";
325                 // Is the group already there?
326                 if (isset($this->subGroups[$subGroupId])) {
327                         // Then throw an exception here
328                         throw new HelperSubGroupAlreadyCreatedException(array($this, $subGroupId), self::EXCEPTION_SUB_GROUP_ALREADY_FOUND);
329                 } // END - if
330
331                 // Count one up
332                 $this->totalCounter++;
333
334                 // Add the group to the stack
335                 $this->subGroups[$this->totalCounter] = $subGroupId;
336                 $this->subGroups[$subGroupId]['opened']  = TRUE;
337                 $this->subGroups[$subGroupId]['content'] = sprintf("<!-- sub-group %s opened (length: %s, tag: %s) //-->%s\n", $subGroupId, strlen($content), $tag, $content);
338                 $this->subGroups[$subGroupId]['tag'] = $tag;
339
340                 // Mark this group as previously opened
341                 $this->setPreviousSubGroupId($subGroupId);
342         }
343
344         /**
345          * Closes the previously opened sub group by added given content to it or
346          * throws an exception if no previous sub group was opened
347          *
348          * @param       $content        Content for previously opened sub group, or leave empty to use div/span of openener
349          * @return      void
350          * @throws      HelperNoPreviousOpenedSubGroupException If no previously opened sub group was found
351          */
352         public function closePreviousSubGroupByContent ($content = '') {
353                 // Check if any sub group was opened before
354                 if ($this->ifSubGroupOpenedPreviously() === FALSE) {
355                         // Then throw an exception
356                         throw new HelperNoPreviousOpenedSubGroupException(array($this, $content), self::EXCEPTION_NO_PREVIOUS_SUB_GROUP_OPENED);
357                 } // END - if
358
359                 // Get previous sub group
360                 $subGroupId = $this->getPreviousSubGroupId();
361
362                 // Is the content empty?
363                 if ((empty($content)) && (!empty($this->subGroups[$subGroupId]['tag']))) {
364                         // Get it from opener
365                         $content = sprintf('<!-- sub-group %s auto-closed //--></%s>', $subGroupId, $this->subGroups[$subGroupId]['tag']);
366                 } // END - if
367
368                 // Add content to it and mark it as closed
369                 $this->subGroups[$subGroupId]['content'] .= sprintf('<!-- sub-group %s closed (length: %s, tag: %s) //-->%s' . PHP_EOL, $subGroupId, strlen($content), $this->subGroups[$subGroupId]['tag'], $content);
370                 $this->subGroups[$subGroupId]['opened'] = FALSE
371                 ;
372
373                 // Mark previous sub group as closed
374                 $this->setPreviousSubGroupId('');
375                 //* DEBUG: */ echo "CLOSE:subGroupId={$subGroupId}<br />\n";
376         }
377
378         /**
379          * Renders all group and sub group in their order
380          *
381          * @return      $content        Rendered HTML content
382          */
383         public function renderContent () {
384                 // Initialize content
385                 $content = '';
386
387                 // Is header content there?
388                 if (isset($this->groups['header'])) {
389                         // Then add it
390                         $content .= $this->groups['header']['content'] . PHP_EOL;
391                 } // END - if
392
393                 // Initiate content
394                 $content .= $this->getContent();
395
396                 // Now "walk" through all groups and sub-groups
397                 for ($idx = 1; $idx <= $this->totalCounter; $idx++) {
398                         // Is this a sub/group and is it closed?
399                         if ((isset($this->groups[$idx])) && ($this->groups[$this->groups[$idx]]['opened'] === FALSE)) {
400                                 // Then add it's content
401                                 $groupContent = trim($this->groups[$this->groups[$idx]]['content']);
402                                 //* DEBUG: */ echo "group={$this->groups[$idx]},content=<pre>".htmlentities($groupContent)."</pre><br />\n";
403                                 $content .= $groupContent;
404                         } elseif ((isset($this->subGroups[$idx])) && ($this->subGroups[$this->subGroups[$idx]]['opened'] === FALSE)) {
405                                 // Then add it's content
406                                 $subGroupContent = $this->subGroups[$this->subGroups[$idx]]['content'];
407                                 //* DEBUG: */ echo "subgroup={$this->subGroups[$idx]},content=<pre>".htmlentities($subGroupContent)."</pre><br />\n";
408                                 $content .= trim($subGroupContent);
409                         } else {
410                                 // Something went wrong
411                                 $this->debugInstance(__METHOD__ . '(): Something unexpected happened here.');
412                         }
413                 } // END - for
414
415                 // Is footer content there?
416                 if (isset($this->groups['footer'])) {
417                         // Then add it
418                         $content .= $this->groups['footer']['content'] . PHP_EOL;
419                 } // END - if
420
421                 // Return it
422                 //* DEBUG: */ echo "content=<pre>".htmlentities($content)."</pre> (".strlen($content).")<br />\n";
423                 return $content;
424         }
425
426         /**
427          * Checks whether the specified group is opened
428          *
429          * @param       $groupId        Id of group to check
430          * @return      $isOpened       Whether the specified group is open
431          */
432         protected function ifGroupIsOpened ($groupId) {
433                 // Is the group open?
434                 $isOpened = ((isset($this->groups[$groupId])) && ($this->groups[$groupId]['opened'] === TRUE));
435
436                 // Return status
437                 return $isOpened;
438         }
439
440         /**
441          * Getter for direct field values
442          *
443          * @param       $fieldName              Name of the field we shall fetch
444          * @return      $fieldValue             Value from field
445          * @throws      NullPointerException    Thrown if $valueInstance is null
446          */
447         public function getValueField ($fieldName) {
448                 // The $valueInstance attribute should not be null!
449                 if (is_null($this->getValueInstance())) {
450                         // Throws an exception here
451                         throw new NullPointerException($this, self::EXCEPTION_IS_NULL_POINTER);
452                 } // END - if
453
454                 // Get the field value
455                 $fieldValue = $this->getValueInstance()->getField($fieldName);
456                 //* DEBUG: */ self::createDebugInstance(__CLASS__)->debugOutput($fieldName.'[]='.gettype($fieldValue).'('.strlen($fieldValue).')');
457
458                 // Is it null?
459                 if ((is_null($fieldValue)) && (!is_null($this->extraInstance))) {
460                         // So try the extra instance
461                         $fieldValue = $this->extraInstance->getField($fieldName);
462                 } // END - if
463
464                 // Return it
465                 return $fieldValue;
466         }
467
468         /**
469          * Getter for value instance
470          *
471          * @return      $valueInstance  Instance of the class holding our values
472          */
473         public final function getValueInstance () {
474                 return $this->valueInstance;
475         }
476
477         /**
478          * Check whether a group was opened previously
479          *
480          * @return      $groupOpened    Whether any group was opened before
481          */
482         protected final function ifGroupOpenedPreviously () {
483                 $groupOpened = (!empty($this->previousGroupId));
484                 return $groupOpened;
485         }
486
487         /**
488          * Check whether a group was opened previously
489          *
490          * @return      $subGroupOpened         Whether any group was opened before
491          */
492         protected final function ifSubGroupOpenedPreviously () {
493                 $subGroupOpened = (!empty($this->previousSubGroupId));
494                 return $subGroupOpened;
495         }
496
497         /**
498          * Getter for previous group id
499          *
500          * @return      $previousGroupId        Id of previously opened group
501          */
502         protected final function getPreviousGroupId () {
503                 return $this->previousGroupId;
504         }
505
506         /**
507          * Setter for previous group id
508          *
509          * @param       $previousGroupId        Id of previously opened group
510          * @return      void
511          */
512         protected final function setPreviousGroupId ($previousGroupId) {
513                 $this->previousGroupId = (string) $previousGroupId;
514         }
515
516         /**
517          * Getter for previous sub group id
518          *
519          * @return      $previousSubGroupId             Id of previously opened sub group
520          */
521         protected final function getPreviousSubGroupId () {
522                 return $this->previousSubGroupId;
523         }
524
525         /**
526          * Setter for previous sub group id
527          *
528          * @param       $previousSubGroupId             Id of previously opened sub group
529          * @return      void
530          */
531         protected final function setPreviousSubGroupId ($previousSubGroupId) {
532                 $this->previousSubGroupId = (string) $previousSubGroupId;
533         }
534 }
535
536 // [EOF]
537 ?>