Seditio Source
Root |
./othercms/phpBB3/vendor/s9e/text-formatter/src/Configurator/TemplateNormalizations/OptimizeChoose.php
<?php

/**
* @package   s9e\TextFormatter
* @copyright Copyright (c) 2010-2021 The s9e authors
* @license   http://www.opensource.org/licenses/mit-license.php The MIT License
*/
namespace s9e\TextFormatter\Configurator\TemplateNormalizations;

use
DOMElement;

class
OptimizeChoose extends AbstractChooseOptimization
{
   
/**
    * Adopt the children of given element's only child
    *
    * @param  DOMElement $branch
    * @return void
    */
   
protected function adoptChildren(DOMElement $branch)
    {
        while (
$branch->firstChild->firstChild)
        {
           
$branch->appendChild($branch->firstChild->removeChild($branch->firstChild->firstChild));
        }
       
$branch->removeChild($branch->firstChild);
    }

   
/**
    * Test whether all branches of current xsl:choose element share a common firstChild/lastChild
    *
    * @param  string $childType Either firstChild or lastChild
    * @return bool
    */
   
protected function matchBranches($childType)
    {
       
$branches = $this->getBranches();
        if (!isset(
$branches[0]->$childType))
        {
            return
false;
        }

       
$childNode = $branches[0]->$childType;
        foreach (
$branches as $branch)
        {
            if (!isset(
$branch->$childType) || !$this->isEqualNode($childNode, $branch->$childType))
            {
                return
false;
            }
        }

        return
true;
    }

   
/**
    * Test whether all branches of current xsl:choose element have a single child with the same start tag
    *
    * @return bool
    */
   
protected function matchOnlyChild()
    {
       
$branches = $this->getBranches();
        if (!isset(
$branches[0]->firstChild))
        {
            return
false;
        }

       
$firstChild = $branches[0]->firstChild;
        if (
$this->isXsl($firstChild, 'choose'))
        {
           
// Abort on xsl:choose because we can't move it without moving its children
           
return false;
        }

        foreach (
$branches as $branch)
        {
            if (
$branch->childNodes->length !== 1 || !($branch->firstChild instanceof DOMElement))
            {
                return
false;
            }
            if (!
$this->isEqualTag($firstChild, $branch->firstChild))
            {
                return
false;
            }
        }

        return
true;
    }

   
/**
    * Move the firstChild of each branch before current xsl:choose
    *
    * @return void
    */
   
protected function moveFirstChildBefore()
    {
       
$branches = $this->getBranches();
       
$this->choose->parentNode->insertBefore(array_pop($branches)->firstChild, $this->choose);
        foreach (
$branches as $branch)
        {
           
$branch->removeChild($branch->firstChild);
        }
    }

   
/**
    * Move the lastChild of each branch after current xsl:choose
    *
    * @return void
    */
   
protected function moveLastChildAfter()
    {
       
$branches = $this->getBranches();
       
$node     = array_pop($branches)->lastChild;
        if (isset(
$this->choose->nextSibling))
        {
           
$this->choose->parentNode->insertBefore($node, $this->choose->nextSibling);
        }
        else
        {
           
$this->choose->parentNode->appendChild($node);
        }
        foreach (
$branches as $branch)
        {
           
$branch->removeChild($branch->lastChild);
        }
    }

   
/**
    * {@inheritdoc}
    */
   
protected function optimizeChoose()
    {
        if (
$this->hasOtherwise())
        {
           
$this->optimizeCommonFirstChild();
           
$this->optimizeCommonLastChild();
           
$this->optimizeCommonOnlyChild();
           
$this->optimizeEmptyBranch();
           
$this->optimizeEmptyOtherwise();
        }
        if (
$this->isEmpty())
        {
           
$this->choose->parentNode->removeChild($this->choose);
        }
        else
        {
           
$this->optimizeSingleBranch();
        }
    }

   
/**
    * Optimize current xsl:choose by moving out the first child of each branch if they match
    *
    * @return void
    */
   
protected function optimizeCommonFirstChild()
    {
        while (
$this->matchBranches('firstChild'))
        {
           
$this->moveFirstChildBefore();
        }
    }

   
/**
    * Optimize current xsl:choose by moving out the last child of each branch if they match
    *
    * @return void
    */
   
protected function optimizeCommonLastChild()
    {
        while (
$this->matchBranches('lastChild'))
        {
           
$this->moveLastChildAfter();
        }
    }

   
/**
    * Optimize current xsl:choose by moving out only child of each branch if they match
    *
    * This will reorder xsl:choose/xsl:when/div into div/xsl:choose/xsl:when if every branch has
    * the same only child (excluding the child's own descendants)
    *
    * @return void
    */
   
protected function optimizeCommonOnlyChild()
    {
        while (
$this->matchOnlyChild())
        {
           
$this->reparentChild();
        }
    }

   
/**
    * Switch the logic of an xsl:otherwise if the only other branch is empty
    *
    * @return void
    */
   
protected function optimizeEmptyBranch()
    {
       
$query = 'count(xsl:when) = 1 and count(xsl:when/node()) = 0 and xsl:otherwise';
        if (!
$this->xpath->evaluate($query, $this->choose))
        {
            return;
        }

       
// test="@foo" becomes test="not(@foo)"
       
$when = $this->xpath('xsl:when', $this->choose)[0];
       
$when->setAttribute('test', 'not(' . $when->getAttribute('test') . ')');

       
$otherwise = $this->xpath('xsl:otherwise', $this->choose)[0];
        while (
$otherwise->firstChild)
        {
           
$when->appendChild($otherwise->removeChild($otherwise->firstChild));
        }
    }

   
/**
    * Optimize away the xsl:otherwise child of current xsl:choose if it's empty
    *
    * @return void
    */
   
protected function optimizeEmptyOtherwise()
    {
       
$query = 'xsl:otherwise[count(node()) = 0]';
        foreach (
$this->xpath($query, $this->choose) as $otherwise)
        {
           
$this->choose->removeChild($otherwise);
        }
    }

   
/**
    * Replace current xsl:choose with xsl:if if it has only one branch
    *
    * @return void
    */
   
protected function optimizeSingleBranch()
    {
       
$query = 'count(xsl:when) = 1 and not(xsl:otherwise)';
        if (!
$this->xpath->evaluate($query, $this->choose))
        {
            return;
        }
       
$when = $this->xpath('xsl:when', $this->choose)[0];
       
$if   = $this->createElement('xsl:if');
       
$if->setAttribute('test', $when->getAttribute('test'));
        while (
$when->firstChild)
        {
           
$if->appendChild($when->removeChild($when->firstChild));
        }

       
$this->choose->parentNode->replaceChild($if, $this->choose);
    }

   
/**
    * Reorder the current xsl:choose tree to make it a child of the first child of its first branch
    *
    * This will reorder xsl:choose/xsl:when/div into div/xsl:choose/xsl:when
    *
    * @return void
    */
   
protected function reparentChild()
    {
       
$branches  = $this->getBranches();
       
$childNode = $branches[0]->firstChild->cloneNode();
       
$childNode->appendChild($this->choose->parentNode->replaceChild($childNode, $this->choose));

        foreach (
$branches as $branch)
        {
           
$this->adoptChildren($branch);
        }
    }
}