<?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;
use DOMText;
class OptimizeChooseText extends AbstractChooseOptimization
{
/**
* Adjust length of the text nodes of current xsl:choose element's branches
*
* @param string $childType Either firstChild or lastChild
* @param integer $pos
* @param integer $len
* @return void
*/
protected function adjustTextNodes($childType, $pos, $len = PHP_INT_MAX)
{
foreach ($this->getBranches() as $branch)
{
$node = $branch->$childType;
$node->nodeValue = substr($node->textContent, $pos, $len);
}
}
/**
* Compute the number of leading characters common to all strings
*
* @param string[] $strings
* @return integer
*/
protected function getPrefixLength(array $strings)
{
$i = 0;
$len = 0;
$maxLen = min(array_map('strlen', $strings));
while ($i < $maxLen)
{
$c = $strings[0][$i];
foreach ($strings as $string)
{
if ($string[$i] !== $c)
{
break 2;
}
}
$len = ++$i;
}
return $len;
}
/**
* Get the text content of the firstChild/lastChild of each branch if they are all text nodes
*
* @param string $childType Either firstChild or lastChild
* @return string[] List of strings or an empty array
*/
protected function getTextContent($childType)
{
$strings = [];
foreach ($this->getBranches() as $branch)
{
if (!($branch->$childType instanceof DOMText))
{
return [];
}
$strings[] = $branch->$childType->textContent;
}
return $strings;
}
/**
* {@inheritdoc}
*/
protected function optimizeChoose()
{
if (!$this->hasOtherwise())
{
return;
}
$this->optimizeLeadingText();
$this->optimizeTrailingText();
}
/**
* Move common leading text outside of current choose
*
* @return void
*/
protected function optimizeLeadingText()
{
$strings = $this->getTextContent('firstChild');
if (empty($strings))
{
return;
}
$len = $this->getPrefixLength($strings);
if ($len)
{
$this->adjustTextNodes('firstChild', $len);
$this->choose->parentNode->insertBefore(
$this->createText(substr($strings[0], 0, $len)),
$this->choose
);
}
}
/**
* Move common trailing text outside of current choose
*
* @return void
*/
protected function optimizeTrailingText()
{
$strings = $this->getTextContent('lastChild');
if (empty($strings))
{
return;
}
// Flip the strings before computing the prefix length to get the suffix length
$len = $this->getPrefixLength(array_map('strrev', $strings));
if ($len)
{
$this->adjustTextNodes('lastChild', 0, -$len);
$this->choose->parentNode->insertBefore(
$this->createText(substr($strings[0], -$len)),
$this->choose->nextSibling
);
}
}
}