3 * A Menu template engine class
5 * @author Roland Haeder <webmaster@ship-simu.org>
7 * @copyright Copyright (c) 2007, 2008 Roland Haeder, 2009 - 2012 Core Developer Team
8 * @license GNU GPL 3.0 or any newer version
9 * @link http://www.ship-simu.org
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.
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.
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/>.
24 class MenuTemplateEngine extends BaseTemplateEngine implements CompileableTemplate {
26 * Main nodes in the XML tree ('menu' is ignored)
28 private $mainNodes = array(
33 * Sub nodes in the XML tree
35 private $subNodes = array(
63 private $menuInstance = NULL;
68 private $curr = array();
71 * Content from dependency
73 private $dependencyContent = array();
76 * Protected constructor
80 protected function __construct () {
81 // Call parent constructor
82 parent::__construct(__CLASS__);
86 * Creates an instance of the class TemplateEngine and prepares it for usage
88 * @param $menuInstance A RenderableMenu instance
89 * @return $templateInstance An instance of TemplateEngine
90 * @throws BasePathIsEmptyException If the provided $templateBasePath is empty
91 * @throws InvalidBasePathStringException If $templateBasePath is no string
92 * @throws BasePathIsNoDirectoryException If $templateBasePath is no
93 * directory or not found
94 * @throws BasePathReadProtectedException If $templateBasePath is
97 public static final function createMenuTemplateEngine (RenderableMenu $menuInstance) {
99 $templateInstance = new MenuTemplateEngine();
101 // Get the application instance from registry
102 $applicationInstance = Registry::getRegistry()->getInstance('app');
104 // Determine base path
105 $templateBasePath = $templateInstance->getConfigInstance()->getConfigEntry('application_base_path') . $applicationInstance->getRequestInstance()->getRequestElement('app') . '/';
107 // Is the base path valid?
108 if (empty($templateBasePath)) {
109 // Base path is empty
110 throw new BasePathIsEmptyException($templateInstance, self::EXCEPTION_UNEXPECTED_EMPTY_STRING);
111 } elseif (!is_string($templateBasePath)) {
113 throw new InvalidBasePathStringException(array($templateInstance, $templateBasePath), self::EXCEPTION_INVALID_STRING);
114 } elseif (!is_dir($templateBasePath)) {
116 throw new BasePathIsNoDirectoryException(array($templateInstance, $templateBasePath), self::EXCEPTION_INVALID_PATH_NAME);
117 } elseif (!is_readable($templateBasePath)) {
119 throw new BasePathReadProtectedException(array($templateInstance, $templateBasePath), self::EXCEPTION_READ_PROTECED_PATH);
123 $templateInstance->setTemplateBasePath($templateBasePath);
125 // Set template extensions
126 $templateInstance->setRawTemplateExtension($templateInstance->getConfigInstance()->getConfigEntry('raw_template_extension'));
127 $templateInstance->setCodeTemplateExtension($templateInstance->getConfigInstance()->getConfigEntry('menu_template_extension'));
129 // Absolute output path for compiled templates
130 $templateInstance->setCompileOutputPath($templateInstance->getConfigInstance()->getConfigEntry('base_path') . $templateInstance->getConfigInstance()->getConfigEntry('compile_output_path'));
132 // Set the menu instance
133 $templateInstance->setMenuInstance($menuInstance);
135 // Init a variable stacker
136 $stackerInstance = ObjectFactory::createObjectByConfiguredName('menu_stacker_class');
139 $templateInstance->setStackerInstance($stackerInstance);
141 // Return the prepared instance
142 return $templateInstance;
146 * Load a specified menu template into the engine
148 * @param $template The menu template we shall load which is
149 * located in 'menu' by default
152 public function loadMenuTemplate ($template) {
154 $this->setTemplateType($this->getConfigInstance()->getConfigEntry('menu_template_type'));
156 // Load the special template
157 $this->loadTemplate($template);
161 * Getter for current main node
163 * @return $currMainNode Current main node
165 public final function getCurrMainNode () {
166 return $this->curr['main_node'];
170 * Setter for current main node
172 * @param $element Element name to set as current main node
173 * @return $currMainNode Current main node
175 private final function setCurrMainNode ($element) {
176 $this->curr['main_node'] = (string) $element;
180 * Getter for main node array
182 * @return $mainNodes Array with valid main node names
184 public final function getMainNodes () {
185 return $this->mainNodes;
189 * Getter for sub node array
191 * @return $subNodes Array with valid sub node names
193 public final function getSubNodes () {
194 return $this->subNodes;
198 * Handles the start element of an XML resource
200 * @param $resource XML parser resource (currently ignored)
201 * @param $element The element we shall handle
202 * @param $attributes All attributes
204 * @throws InvalidXmlNodeException If an unknown/invalid XML node name was found
206 public function startElement ($resource, $element, array $attributes) {
207 // Initial method name which will never be called...
208 $methodName = 'initMenu';
210 // Make the element name lower-case
211 $element = strtolower($element);
213 // Is the element a main node?
214 //* DEBUG: */ echo "START: >".$element."<<br />\n";
215 if (in_array($element, $this->getMainNodes())) {
216 // Okay, main node found!
217 $methodName = 'start' . $this->convertToClassName($element);
220 $this->setCurrMainNode($element);
221 } elseif (in_array($element, $this->getSubNodes())) {
223 $methodName = 'start' . $this->convertToClassName($element);
224 } elseif ($element != 'menu') {
225 // Invalid node name found
226 throw new InvalidXmlNodeException(array($this, $element, $attributes), XmlParser::EXCEPTION_XML_NODE_UNKNOWN);
230 //* DEBUG: */ echo "call: ".$methodName."<br />\n";
231 call_user_func_array(array($this, $methodName), $attributes);
235 * Ends the main or sub node by sending out the gathered data
237 * @param $resource An XML resource pointer (currently ignored)
238 * @param $nodeName Name of the node we want to finish
240 * @throws XmlNodeMismatchException If current main node mismatches the closing one
242 public function endElement ($resource, $nodeName) {
243 // Make all lower-case
244 $nodeName = strtolower($nodeName);
246 // Does this match with current main node?
247 //* DEBUG: */ echo "END: >".$nodeName."<<br />\n";
248 if (($nodeName != $this->getCurrMainNode()) && (in_array($nodeName, $this->getMainNodes()))) {
250 throw new XmlNodeMismatchException (array($this, $nodeName, $this->getCurrMainNode()), XmlParser::EXCEPTION_XML_NODE_MISMATCH);
253 // Construct method name
254 $methodName = 'finish' . $this->convertToClassName($nodeName);
256 // Call the corresponding method
257 //* DEBUG: */ echo "call: ".$methodName."<br />\n";
258 call_user_func_array(array($this, $methodName), array());
264 * @param $resource XML parser resource (currently ignored)
265 * @param $characters Characters to handle
267 * @todo Find something useful with this!
269 public function characterHandler ($resource, $characters) {
270 // Trim all spaces away
271 $characters = trim($characters);
273 // Is this string empty?
274 if (empty($characters)) {
275 // Then skip it silently
279 // Assign the found characters to variable and use the last entry from
281 parent::assignVariable($this->getStackerInstance()->getNamed('current_node'), $characters);
285 * Handles the template dependency for given node
287 * @param $node The node we should load a dependency template
288 * @param $templateDependency A template to load to satisfy dependencies
291 private function handleTemplateDependency ($node, $templateDependency) {
292 // Is the template dependency set?
293 if ((!empty($templateDependency)) && (!isset($this->dependencyContent[$node]))) {
294 // Get a temporay menu template instance
295 $templateInstance = ObjectFactory::createObjectByConfiguredName('menu_template_class', array($this->getMenuInstance()));
298 $templateInstance->loadMenuTemplate($templateDependency);
300 // Parse the XML content
301 $templateInstance->renderXmlContent();
303 // Save the parsed raw content in our dependency array
304 $this->dependencyContent[$node] = $templateInstance->getRawTemplateData();
309 * Intializes the menu
311 * @param $templateDependency A template to load to satisfy dependencies
313 * @todo Add cache creation here
315 private function initMenu ($templateDependency = '') {
316 // Get web template engine
317 $this->setTemplateInstance(ObjectFactory::createObjectByConfiguredName('web_template_class', array($this->getApplicationInstance())));
319 // Handle the dependency template
320 $this->handleTemplateDependency('menu', $templateDependency);
322 // Push the node name on the stacker
323 $this->getStackerInstance()->pushNamed('current_node', 'menu');
327 * Starts the menu entries
329 * @param $templateDependency A template to load to satisfy dependencies
332 private function startEntryList () {
333 // Push the node name on the stacker
334 $this->getStackerInstance()->pushNamed('current_node', 'entry-list');
338 * Starts the menu block header
342 private function startBlockHeader () {
343 // Push the node name on the stacker
344 $this->getStackerInstance()->pushNamed('current_node', 'block-header');
348 * Starts the menu block footer
352 private function startBlockFooter () {
353 // Push the node name on the stacker
354 $this->getStackerInstance()->pushNamed('current_node', 'block-footer');
358 * Starts the menu property 'block-list'
362 private function startBlockList () {
363 // Push the node name on the stacker
364 $this->getStackerInstance()->pushNamed('current_node', 'block-list');
368 * Starts the menu property 'block'
372 private function startBlock () {
373 // Push the node name on the stacker
374 $this->getStackerInstance()->pushNamed('current_node', 'block');
378 * Starts the menu property 'title'
382 private function startTitle () {
383 // Push the node name on the stacker
384 $this->getStackerInstance()->pushNamed('current_node', 'title');
388 * Starts the menu property 'title-id'
392 private function startTitleId () {
393 // Push the node name on the stacker
394 $this->getStackerInstance()->pushNamed('current_node', 'title-id');
398 * Starts the menu property 'title-class'
402 private function startTitleClass () {
403 // Push the node name on the stacker
404 $this->getStackerInstance()->pushNamed('current_node', 'title-class');
408 * Starts the menu property 'title-text'
412 private function startTitleText () {
413 // Push the node name on the stacker
414 $this->getStackerInstance()->pushNamed('current_node', 'title-text');
418 * Starts the menu property 'entry'
422 private function startEntry () {
423 // Push the node name on the stacker
424 $this->getStackerInstance()->pushNamed('current_node', 'entry');
428 * Starts the menu property 'entry-id'
432 private function startEntryId () {
433 // Push the node name on the stacker
434 $this->getStackerInstance()->pushNamed('current_node', 'entry-id');
438 * Starts the menu property 'anchor'
442 private function startAnchor () {
443 // Push the node name on the stacker
444 $this->getStackerInstance()->pushNamed('current_node', 'anchor');
448 * Starts the menu property 'anchor-id'
452 private function startAnchorId () {
453 // Push the node name on the stacker
454 $this->getStackerInstance()->pushNamed('current_node', 'anchor-id');
458 * Starts the menu property 'anchor-text'
462 private function startAnchorText () {
463 // Push the node name on the stacker
464 $this->getStackerInstance()->pushNamed('current_node', 'anchor-text');
468 * Starts the menu property 'anchor-title'
472 private function startAnchorTitle () {
473 // Push the node name on the stacker
474 $this->getStackerInstance()->pushNamed('current_node', 'anchor-title');
478 * Starts the menu property 'anchor-href'
482 private function startAnchorHref () {
483 // Push the node name on the stacker
484 $this->getStackerInstance()->pushNamed('current_node', 'anchor-href');
488 * Starts the menu property 'footer-id'
492 private function startFooterId () {
493 // Push the node name on the stacker
494 $this->getStackerInstance()->pushNamed('current_node', 'footer-id');
498 * Starts the menu property 'footer-class'
502 private function startFooterClass () {
503 // Push the node name on the stacker
504 $this->getStackerInstance()->pushNamed('current_node', 'footer-class');
508 * Starts the menu property 'footer-text'
512 private function startFooterText () {
513 // Push the node name on the stacker
514 $this->getStackerInstance()->pushNamed('current_node', 'footer-text');
518 * Finishes the title node by added another template to the menu
522 private function finishTitle () {
523 // Pop the last entry
524 $this->getStackerInstance()->popNamed('current_node');
528 * Finishes the title-id node by
532 private function finishTitleId () {
533 // Pop the last entry
534 $this->getStackerInstance()->popNamed('current_node');
538 * Finishes the title-class node
542 private function finishTitleClass () {
543 // Pop the last entry
544 $this->getStackerInstance()->popNamed('current_node');
548 * Finishes the title-class node
552 private function finishTitleText () {
553 // Pop the last entry
554 $this->getStackerInstance()->popNamed('current_node');
558 * Finishes the footer-text node
562 private function finishFooterText () {
563 // Pop the last entry
564 $this->getStackerInstance()->popNamed('current_node');
568 * Finishes the footer-class node
572 private function finishFooterClass () {
573 // Pop the last entry
574 $this->getStackerInstance()->popNamed('current_node');
578 * Finishes the footer-id node
582 private function finishFooterId () {
583 // Pop the last entry
584 $this->getStackerInstance()->popNamed('current_node');
588 * Finishes the anchor-href node
592 private function finishAnchorHref () {
593 // Pop the last entry
594 $this->getStackerInstance()->popNamed('current_node');
598 * Finishes the anchor-title node
602 private function finishAnchorTitle () {
603 // Pop the last entry
604 $this->getStackerInstance()->popNamed('current_node');
608 * Finishes the anchor-text node
612 private function finishAnchorText () {
613 // Pop the last entry
614 $this->getStackerInstance()->popNamed('current_node');
618 * Finishes the anchor-id node
622 private function finishAnchorId () {
623 // Pop the last entry
624 $this->getStackerInstance()->popNamed('current_node');
628 * Finishes the anchor node
632 private function finishAnchor () {
633 // Pop the last entry
634 $this->getStackerInstance()->popNamed('current_node');
638 * Finishes the entry-id node
642 private function finishEntryId () {
643 // Pop the last entry
644 $this->getStackerInstance()->popNamed('current_node');
648 * Finishes the entry node
652 private function finishEntry () {
653 // Pop the last entry
654 $this->getStackerInstance()->popNamed('current_node');
658 * Finishes the block node
662 private function finishBlock () {
663 // Pop the last entry
664 $this->getStackerInstance()->popNamed('current_node');
668 * Finishes the block-list node
672 private function finishBlockList () {
673 // Pop the last entry
674 $this->getStackerInstance()->popNamed('current_node');
678 * Finishes the menu entries
682 private function finishEntryList () {
683 // Pop the last entry
684 $this->getStackerInstance()->popNamed('current_node');
688 * Finishes the menu block header
692 private function finishBlockHeader () {
693 // Pop the last entry
694 $this->getStackerInstance()->popNamed('current_node');
698 * Finishes the menu block footer
702 private function finishBlockFooter () {
703 // Pop the last entry
704 $this->getStackerInstance()->popNamed('current_node');
712 private function finishMenu () {
713 // Pop the last entry
714 $this->getStackerInstance()->popNamed('current_node');
718 * Getter for menu cache file (FQFN)
720 * @return $fqfn Full-qualified file name of the menu cache
722 public function getMenuCacheFqfn () {
723 // Get the FQFN ready
724 $fqfn = sprintf("%s%s%s/%s.%s",
725 $this->getConfigInstance()->getConfigEntry('base_path'),
726 $this->getGenericBasePath(),
729 $this->getMenuInstance()->getMenuName() . ':' .
730 $this->__toString() . ':' .
731 $this->getMenuInstance()->__toString()
733 $this->getMenuInstance()->getMenuType()