| <?php |
| /** |
| * File containing the ezcTemplateBlockTstNode class |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| * @package Template |
| * @version //autogen// |
| * @license http://www.apache.org/licenses/LICENSE-2.0 Apache License, Version 2.0 |
| * @access private |
| */ |
| /** |
| * Block elements in parser trees. |
| * |
| * @package Template |
| * @version //autogen// |
| * @access private |
| */ |
| class ezcTemplateBlockTstNode extends ezcTemplateCodeTstNode |
| { |
| |
| /** |
| * Array of all child elements. |
| * |
| * @var array |
| */ |
| public $children; |
| |
| /** |
| * Is true if this block is a closing block (ie. {/...}), false otherwise. |
| * @var bool |
| */ |
| public $isClosingBlock; |
| |
| /** |
| * Is true if this block can nest other elements. |
| * A closing block is required to end the nesting in this case. |
| * |
| * @see $isClosingBlock |
| * @var bool |
| */ |
| public $isNestingBlock; |
| |
| /** |
| * The name of the block. |
| * |
| * @var string |
| */ |
| public $name; |
| |
| /** |
| * The calculated indentation value for the entire text block. |
| * |
| * @var string/false |
| */ |
| protected $minimumWhitespace; |
| |
| /** |
| * The parent block element of the current block. |
| * |
| * @var ezcTemplateBlockTstNode |
| */ |
| public $parentBlock; |
| |
| /** |
| * The starting point for the closing marker (the slash) specified with a cursor |
| * |
| * <code> |
| * {/foreach} |
| * ^ |
| * </code> |
| * |
| * @var ezcTemplateCursor |
| */ |
| public $closingCursor; |
| |
| /** |
| * Constructs a new ezcTemplateBlockTstNode |
| * |
| * @param ezcTemplateSourceCode $source |
| * @param ezcTemplateCursor $start |
| * @param ezcTemplateCursor $end |
| */ |
| public function __construct( ezcTemplateSourceCode $source, /*ezcTemplateCursor*/ $start, /*ezcTemplateCursor*/ $end ) |
| { |
| parent::__construct( $source, $start, $end ); |
| $this->children = array(); |
| $this->isClosingBlock = false; |
| $this->parentBlock = null; |
| $this->closingCursor = null; |
| // Most blocks use nesting |
| $this->isNestingBlock = true; |
| $this->minimumWhitespace = null; |
| } |
| |
| /** |
| * Returns the current tree properties |
| * |
| * @return array(string=>mixed) |
| */ |
| public function getTreeProperties() |
| { |
| return array( 'name' => $this->name, |
| 'isClosingBlock' => $this->isClosingBlock, |
| 'isNestingBlock' => $this->isNestingBlock, |
| 'children' => $this->children ); |
| } |
| |
| /** |
| * Checks if the block has any children, returns true or false. |
| * |
| * @return bool |
| */ |
| public function hasChildren() |
| { |
| return count( $this->children ); |
| } |
| |
| /** |
| * Adds the element $child as child of this block element. |
| * |
| * @param ezcTemplateTstNode $child |
| */ |
| public function appendChild( ezcTemplateTstNode $child ) |
| { |
| $this->children[] = $child; |
| } |
| |
| /** |
| * Returns the first child element of the block. |
| * |
| * @return ezcTemplateTstNode |
| * @throws ezcTemplateTstNodeException::NO_FIRST_CHILD if there are no children. |
| */ |
| public function getFirstChild() |
| { |
| if ( count( $this->children ) === 0 ) |
| throw new ezcTemplateTstNodeException( ezcTemplateTstNodeException::NO_FIRST_CHILD ); |
| |
| return $this->children[0]; |
| } |
| |
| /** |
| * Removes the first child element of the block. |
| * |
| * @throws ezcTemplateTstNodeException::NO_FIRST_CHILD if there are no children. |
| */ |
| public function removeFirstChild() |
| { |
| if ( count( $this->children ) === 0 ) |
| throw new ezcTemplateTstNodeException( ezcTemplateTstNodeException::NO_FIRST_CHILD ); |
| |
| array_shift( $this->children ); |
| } |
| |
| /** |
| * Returns the last child element of the block. |
| * |
| * @return ezcTemplateTstNode |
| * @throws ezcTemplateTstNodeException::NO_LAST_CHILD if there are no children. |
| */ |
| public function getLastChild() |
| { |
| if ( count( $this->children ) === 0 ) |
| throw new ezcTemplateTstNodeException( ezcTemplateTstNodeException::NO_LAST_CHILD ); |
| |
| return $this->children[count( $this->children ) - 1]; |
| } |
| |
| /** |
| * Removes the last child element of the block. |
| * |
| * @throws ezcTemplateTstNodeException::NO_LAST_CHILD if there are no children. |
| */ |
| public function removeLastChild() |
| { |
| if ( count( $this->children ) === 0 ) |
| throw new ezcTemplateTstNodeException( ezcTemplateTstNodeException::NO_LAST_CHILD ); |
| |
| array_pop( $this->children ); |
| } |
| |
| /** |
| * Returns true if the block is not meant to start a new nesting level. |
| * |
| * @param ezcTemplateBlockTstNode $block |
| * @return bool |
| */ |
| public function canBeChildOf( ezcTemplateBlockTstNode $block ) |
| { |
| // If the current block cannot start a new nesting level |
| // it means it should be considered an standalone block and |
| // can be a child. |
| if ( !$this->isNestingBlock ) |
| { |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * Callback for closing an open block element. |
| * |
| * This can be used to perform changes to the open block before the closing |
| * block (this) is discarded. |
| * |
| * @param ezcTemplateBlockTstNode $openBlock The block which is currently open. |
| */ |
| public function closeOpenBlock( ezcTemplateBlockTstNode $openBlock ) |
| { |
| // Does nothing, re-implement to perform your duties. |
| } |
| |
| /** |
| * Checks if the element object $element can be handled by the current block. |
| * |
| * This method can be overridden to handle custom elements for instance {else} |
| * blocks of {if} control structures. |
| * Returns true if it can be handled, false otherwise. If it can be handled |
| * the caller must make sure handleElement() is used. |
| * |
| * @param ezcTemplateTstNode $element The element object which should be checked for special handling. |
| * @return bool |
| * Note: This method will be called if the element could not be added as a |
| * child or used to close the block. |
| */ |
| public function canHandleElement( ezcTemplateTstNode $element ) |
| { |
| // Default is to not handle any elements, sub-classes needs to |
| // figure out which ones it can handle. |
| return false; |
| } |
| |
| /** |
| * Empty function |
| * |
| * @todo Can be removed? |
| * |
| * @param mixed $parentElement |
| * @return void |
| */ |
| public function canAttachToParent( $parentElement ) |
| { |
| } |
| |
| /** |
| * Passes control of $element to the current block for special handling. |
| * |
| * The sub-classes need to re-implement canHandleElement() and this method |
| * to handle special cases which are not covered by the general child/closure |
| * rules in the program parser. |
| * Typically block elements which have special child blocks need to |
| * re-implement this. |
| * |
| * @param ezcTemplateTstNode $element The element object which should be handled. |
| * Note: This method will be called if the element could not be added as a |
| * child or used to the block. |
| */ |
| public function handleElement( ezcTemplateTstNode $element ) |
| { |
| // Sub-classes need to implement the special handling here |
| if ( $this === $element ) |
| { |
| throw new ezcTemplateInternalException( "Detected invalid recursion creation in parser element " . get_class( $this ) ); |
| } |
| |
| if ( $element instanceof ezcTemplateLoopTstNode ) |
| { |
| throw new ezcTemplateParserException( $element->source, $element->startCursor, $element->startCursor, ezcTemplateSourceToTstErrorMessages::MSG_UNEXPECTED_BREAK_OR_CONTINUE ); |
| } |
| |
| if ( $element instanceof ezcTemplateConditionBodyTstNode ) |
| { |
| throw new ezcTemplateParserException( $element->source, $element->startCursor, $element->startCursor, |
| sprintf( ezcTemplateSourceToTstErrorMessages::MSG_UNEXPECTED_BLOCK, $element->name ) ); |
| } |
| |
| if ( $element instanceof ezcTemplateCaseTstNode ) |
| { |
| throw new ezcTemplateParserException( $element->source, $element->startCursor, $element->startCursor, |
| sprintf( ezcTemplateSourceToTstErrorMessages::MSG_UNEXPECTED_BLOCK, $element->name ) ); |
| } |
| |
| |
| |
| $this->children[] = $element; |
| |
| return true; |
| |
| } |
| |
| /** |
| * {@inheritdoc} |
| * Returns the minimum column of all children. |
| */ |
| public function minimumWhitespaceColumn() |
| { |
| if ( $this->minimumWhitespace !== null ) |
| return $this->minimumWhitespace; |
| |
| $this->minimumWhitespace = $this->minimumElementsColumn( $this->children ); |
| return $this->minimumWhitespace; |
| } |
| |
| /** |
| * Finds the minimum whitespace column for all the specified elements. |
| * |
| * This method is useful for sub-classes of the block element in case they have |
| * multiple element lists and need to fetch the column value from all of them. |
| * |
| * @param array(ezcTemplateTstNode) $elements Array of element objects to check. |
| * @return int/false |
| */ |
| protected function minimumElementsColumn( Array $elements ) |
| { |
| $minimum = false; |
| foreach ( $elements as $element ) |
| { |
| $column = $element->minimumWhitespaceColumn(); |
| // If the column is false it means there it only contains whitespace |
| // or could figure out a minimum column |
| if ( $column === false ) |
| continue; |
| |
| if ( $minimum === false ) |
| { |
| $minimum = $column; |
| } |
| else |
| { |
| $minimum = min( $minimum, $column ); |
| } |
| } |
| return $minimum; |
| } |
| |
| /** |
| * Trims away the minimum indentation for the current block. |
| * |
| * Note: If a sub-class of this block element class uses another variable |
| * than $children for child elements or uses multiple lists then it |
| * needs to re-implement this method and call |
| * $removal->trimBlockLevelIndentation() for the correct list. |
| * |
| * @param ezcTemplateWhitespaceRemoval $removal |
| * The removal object which knows how to get rid of indentation for the current level. |
| */ |
| public function trimIndentation( ezcTemplateWhitespaceRemoval $removal ) |
| { |
| // Tell the removal object to trim our children |
| $removal->trimBlockLevelIndentation( $this, $this->children ); |
| } |
| |
| /** |
| * Trims away the whitespace and EOL marker from the block line. |
| * The whitespace and EOL marker is found in the first child element |
| * which must be a text block element. |
| * |
| * Note: If a sub-class of this block element class uses another variable |
| * than $children for child elements or uses multiple lists then it |
| * needs to re-implement this method and call |
| * $removal->trimBlockLine() for the correct list. |
| * |
| * @param ezcTemplateWhitespaceRemoval $removal |
| * The removal object which knows how to get rid of block line whitespace. |
| */ |
| public function trimLine( ezcTemplateWhitespaceRemoval $removal ) |
| { |
| if ( count( $this->children ) == 0 ) |
| return; |
| |
| if ( $this->children[0] instanceof ezcTemplateTextTstNode ) |
| { |
| // Tell the removal object to trim our first text child |
| $removal->trimBlockLine( $this, $this->children[0] ); |
| } |
| |
| // Tell the removal object to trim text blocks after the current block |
| // and after all sub-blocks. |
| $removal->trimBlockLines( $this, $this->children ); |
| } |
| } |
| ?> |