All XML-parsing-related stuff refactured to new XmlParser class
[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@ship-simu.org>
6  * @version             0.0.0
7  * @copyright   Copyright (c) 2007, 2008 Roland Haeder, 2009 Core Developer Team
8  * @license             GNU GPL 3.0 or any newer version
9  * @link                http://www.ship-simu.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) . "\n";
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: */ $this->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: */ $this->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: */ $this->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: */ $this->debugOutput($extraKey.'='.$this->extraInstance.' - EXTRA!');
216                 } // END - if
217
218                 // Is the value instance valid?
219                 if (is_null($this->valueInstance)) {
220                         try {
221                                 // Get the requested instance
222                                 $this->valueInstance = ObjectFactory::createObjectByConfiguredName($registryKey . '_class', array($this->extraInstance));
223                         } catch (FrameworkException $e) {
224                                 // Okay, nothing found so throw a null pointer exception here
225                                 throw new NullPointerException($this, self::EXCEPTION_IS_NULL_POINTER);
226                         }
227                 } // END - if
228         }
229
230         /**
231          * Opens a helper group with given group id and content or throws an
232          * exception if that group is already found regardless if it is open or
233          * closed.
234          *
235          * @param       $groupId        Group id to open
236          * @param       $content        Initial content to add to the group
237          * @param       $tag            HTML tag used to open this group
238          * @return      void
239          * @throws      HelperGroupAlreadyCreatedException      If the group was already created before
240          */
241         protected function openGroupByIdContent ($groupId, $content, $tag) {
242                 //* DEBUG: */ echo "OPEN:groupId={$groupId},content=<pre>".htmlentities($content)."</pre>\n";
243                 // Is the group already there?
244                 if (isset($this->groups[$groupId])) {
245                         // Then throw an exception here
246                         throw new HelperGroupAlreadyCreatedException(array($this, $groupId), self::EXCEPTION_GROUP_ALREADY_FOUND);
247                 } // END - if
248
249                 // Count one up
250                 $this->totalCounter++;
251
252                 // Add the group to the stack
253                 $this->groups[$this->totalCounter] = $groupId;
254                 $this->groups[$groupId]['opened']  = true;
255                 $this->groups[$groupId]['content'] = sprintf(
256                         "<!-- group %s opened (length: %s, tag: %s) //-->%s\n",
257                         $groupId,
258                         strlen($content),
259                         $tag,
260                         $content
261                 );
262                 $this->groups[$groupId]['tag'] = $tag;
263
264                 // Mark this group as previously opened
265                 $this->setPreviousGroupId($groupId);
266         }
267
268         /**
269          * Closes the previously opened group by added given content to it or
270          * throws an exception if no previous group was opened
271          *
272          * @param       $content        Content for previously opened group, or empty to use tag of opener
273          * @return      void
274          * @throws      HelperNoPreviousOpenedGroupException    If no previously opened group was found
275          */
276         public function closePreviousGroupByContent ($content = '') {
277                 // Check if any sub group was opened before
278                 if ($this->ifSubGroupOpenedPreviously()) {
279                         // Close it automatically
280                         $this->closePreviousSubGroupByContent();
281                 } // END - if
282
283                 // Check if any group was opened before
284                 if ($this->ifGroupOpenedPreviously() === false) {
285                         // Then throw an exception
286                         throw new HelperNoPreviousOpenedGroupException(array($this, $content), self::EXCEPTION_NO_PREVIOUS_SUB_GROUP_OPENED);
287                 } // END - if
288
289                 // Get previous group
290                 $groupId = $this->getPreviousGroupId();
291
292                 // Is the content empty?
293                 if ((empty($content)) && (!empty($this->groups[$groupId]['tag']))) {
294                         // Get it from opener
295                         $content = sprintf(
296                                 "<!-- group %s auto-closed //--></%s>",
297                                 $groupId,
298                                 $this->groups[$groupId]['tag']
299                         );
300                 } // END - if
301
302                 // Add content to it and mark it as closed
303                 $this->groups[$groupId]['content'] .= sprintf(
304                         "<!-- group %s closed (length: %s, tag: %s) //-->%s\n",
305                         $groupId,
306                         strlen($content),
307                         $this->groups[$groupId]['tag'],
308                         $content
309                 );
310                 $this->groups[$groupId]['opened'] = false;
311
312                 // Mark previous group as closed
313                 $this->setPreviousGroupId('');
314                 //* DEBUG: */ echo "CLOSE:groupId={$groupId}<br />\n";
315         }
316
317         /**
318          * Opens a helper sub group with given group id and content or throws an
319          * exception if that sub group is already found regardless if it is open or
320          * closed.
321          *
322          * @param       $subGroupId             Sub group id to open
323          * @param       $content                Initial content to add to the sub group
324          * @param       $tag                    HTML tag used to open this group
325          * @return      void
326          * @throws      HelperSubGroupAlreadyCreatedException   If the sub group was already created before
327          */
328         protected function openSubGroupByIdContent ($subGroupId, $content, $tag) {
329                 //* DEBUG: */ echo "OPEN:subGroupId={$subGroupId},content=".htmlentities($content)."<br />\n";
330                 // Is the group already there?
331                 if (isset($this->subGroups[$subGroupId])) {
332                         // Then throw an exception here
333                         throw new HelperSubGroupAlreadyCreatedException(array($this, $subGroupId), self::EXCEPTION_SUB_GROUP_ALREADY_FOUND);
334                 } // END - if
335
336                 // Count one up
337                 $this->totalCounter++;
338
339                 // Add the group to the stack
340                 $this->subGroups[$this->totalCounter] = $subGroupId;
341                 $this->subGroups[$subGroupId]['opened']  = true;
342                 $this->subGroups[$subGroupId]['content'] = sprintf("<!-- sub-group %s opened (length: %s, tag: %s) //-->%s\n", $subGroupId, strlen($content), $tag, $content);
343                 $this->subGroups[$subGroupId]['tag'] = $tag;
344
345                 // Mark this group as previously opened
346                 $this->setPreviousSubGroupId($subGroupId);
347         }
348
349         /**
350          * Closes the previously opened sub group by added given content to it or
351          * throws an exception if no previous sub group was opened
352          *
353          * @param       $content        Content for previously opened sub group, or leave empty to use div/span of openener
354          * @return      void
355          * @throws      HelperNoPreviousOpenedSubGroupException If no previously opened sub group was found
356          */
357         public function closePreviousSubGroupByContent ($content = '') {
358                 // Check if any sub group was opened before
359                 if ($this->ifSubGroupOpenedPreviously() === false) {
360                         // Then throw an exception
361                         throw new HelperNoPreviousOpenedSubGroupException(array($this, $content), self::EXCEPTION_NO_PREVIOUS_SUB_GROUP_OPENED);
362                 } // END - if
363
364                 // Get previous sub group
365                 $subGroupId = $this->getPreviousSubGroupId();
366
367                 // Is the content empty?
368                 if ((empty($content)) && (!empty($this->subGroups[$subGroupId]['tag']))) {
369                         // Get it from opener
370                         $content = sprintf("<!-- sub-group %s auto-closed //--></%s>", $subGroupId, $this->subGroups[$subGroupId]['tag']);
371                 } // END - if
372
373                 // Add content to it and mark it as closed
374                 $this->subGroups[$subGroupId]['content'] .= sprintf("<!-- sub-group %s closed (length: %s, tag: %s) //-->%s\n", $subGroupId, strlen($content), $this->subGroups[$subGroupId]['tag'], $content);
375                 $this->subGroups[$subGroupId]['opened'] = false;
376
377                 // Mark previous sub group as closed
378                 $this->setPreviousSubGroupId('');
379                 //* DEBUG: */ echo "CLOSE:subGroupId={$subGroupId}<br />\n";
380         }
381
382         /**
383          * Renders all group and sub group in their order
384          *
385          * @return      $content        Rendered HTML content
386          */
387         public function renderContent () {
388                 // Initialize content
389                 $content = '';
390
391                 // Is header content there?
392                 if (isset($this->groups['header'])) {
393                         // Then add it
394                         $content .= $this->groups['header']['content'] . "\n";
395                 } // END - if
396
397                 // Initiate content
398                 $content .= $this->getContent();
399
400                 // Now "walk" through all groups and sub-groups
401                 for ($idx = 1; $idx <= $this->totalCounter; $idx++) {
402                         // Is this a sub/group and is it closed?
403                         if ((isset($this->groups[$idx])) && ($this->groups[$this->groups[$idx]]['opened'] === false)) {
404                                 // Then add it's content
405                                 $groupContent = trim($this->groups[$this->groups[$idx]]['content']);
406                                 //* DEBUG: */ echo "group={$this->groups[$idx]},content=<pre>".htmlentities($groupContent)."</pre><br />\n";
407                                 $content .= $groupContent;
408                         } elseif ((isset($this->subGroups[$idx])) && ($this->subGroups[$this->subGroups[$idx]]['opened'] === false)) {
409                                 // Then add it's content
410                                 $subGroupContent = $this->subGroups[$this->subGroups[$idx]]['content'];
411                                 //* DEBUG: */ echo "subgroup={$this->subGroups[$idx]},content=<pre>".htmlentities($subGroupContent)."</pre><br />\n";
412                                 $content .= trim($subGroupContent);
413                         } else {
414                                 // Something went wrong
415                                 $this->debugInstance(__METHOD__."(): Something unexpected happened here.");
416                         }
417                 } // END - for
418
419                 // Is footer content there?
420                 if (isset($this->groups['footer'])) {
421                         // Then add it
422                         $content .= $this->groups['footer']['content'] . "\n";
423                 } // END - if
424
425                 // Return it
426                 //* DEBUG: */ echo "content=<pre>".htmlentities($content)."</pre> (".strlen($content).")<br />\n";
427                 return $content;
428         }
429
430         /**
431          * Checks wether the specified group is opened
432          *
433          * @param       $groupId        Id of group to check
434          * @return      $isOpened       Wether the specified group is open
435          */
436         protected function ifGroupIsOpened ($groupId) {
437                 // Is the group open?
438                 $isOpened = ((isset($this->groups[$groupId])) && ($this->groups[$groupId]['opened'] === true));
439
440                 // Return status
441                 return $isOpened;
442         }
443
444         /**
445          * Getter for direct field values
446          *
447          * @param       $fieldName              Name of the field we shall fetch
448          * @return      $fieldValue             Value from field
449          * @throws      NullPointerException    Thrown if $valueInstance is null
450          */
451         public function getValueField ($fieldName) {
452                 // The $valueInstance attribute should not be null!
453                 if (is_null($this->getValueInstance())) {
454                         // Throws an exception here
455                         throw new NullPointerException($this, self::EXCEPTION_IS_NULL_POINTER);
456                 } // END - if
457
458                 // Get the field value
459                 $fieldValue = $this->getValueInstance()->getField($fieldName);
460                 //* DEBUG: */ $this->debugOutput($fieldName.'[]='.gettype($fieldValue).'('.strlen($fieldValue).')');
461
462                 // Is it null?
463                 if ((is_null($fieldValue)) && (!is_null($this->extraInstance))) {
464                         // So try the extra instance
465                         $fieldValue = $this->extraInstance->getField($fieldName);
466                 } // END - if
467
468                 // Return it
469                 return $fieldValue;
470         }
471
472         /**
473          * Getter for value instance
474          *
475          * @return      $valueInstance  Instance of the class holding our values
476          */
477         public final function getValueInstance () {
478                 return $this->valueInstance;
479         }
480
481         /**
482          * Check wether a group was opened previously
483          *
484          * @return      $groupOpened    Wether any group was opened before
485          */
486         protected final function ifGroupOpenedPreviously () {
487                 $groupOpened = (!empty($this->previousGroupId));
488                 return $groupOpened;
489         }
490
491         /**
492          * Check wether a group was opened previously
493          *
494          * @return      $subGroupOpened         Wether any group was opened before
495          */
496         protected final function ifSubGroupOpenedPreviously () {
497                 $subGroupOpened = (!empty($this->previousSubGroupId));
498                 return $subGroupOpened;
499         }
500
501         /**
502          * Getter for previous group id
503          *
504          * @return      $previousGroupId        Id of previously opened group
505          */
506         protected final function getPreviousGroupId () {
507                 return $this->previousGroupId;
508         }
509
510         /**
511          * Setter for previous group id
512          *
513          * @param       $previousGroupId        Id of previously opened group
514          * @return      void
515          */
516         protected final function setPreviousGroupId ($previousGroupId) {
517                 $this->previousGroupId = (string) $previousGroupId;
518         }
519
520         /**
521          * Getter for previous sub group id
522          *
523          * @return      $previousSubGroupId             Id of previously opened sub group
524          */
525         protected final function getPreviousSubGroupId () {
526                 return $this->previousSubGroupId;
527         }
528
529         /**
530          * Setter for previous sub group id
531          *
532          * @param       $previousSubGroupId             Id of previously opened sub group
533          * @return      void
534          */
535         protected final function setPreviousSubGroupId ($previousSubGroupId) {
536                 $this->previousSubGroupId = (string) $previousSubGroupId;
537         }
538 }
539
540 // [EOF]
541 ?>