Completed
Push — master ( 713cc2...cff256 )
by Josh
19:36 queued 14:20
created

OptimizeChooseText::optimizeLeadingText()   A

Complexity

Conditions 3
Paths 3

Size

Total Lines 18
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 18
rs 9.4285
c 0
b 0
f 0
cc 3
eloc 10
nc 3
nop 0
1
<?php
2
3
/**
4
* @package   s9e\TextFormatter
5
* @copyright Copyright (c) 2010-2017 The s9e Authors
6
* @license   http://www.opensource.org/licenses/mit-license.php The MIT License
7
*/
8
namespace s9e\TextFormatter\Configurator\TemplateNormalizations;
9
10
use DOMElement;
11
use DOMText;
12
13
class OptimizeChooseText extends AbstractChooseOptimization
14
{
15
	/**
16
	* Adjust length of the text nodes of current xsl:choose element's branches
17
	*
18
	* @param  string  $childType Either firstChild or lastChild
19
	* @param  integer $pos
20
	* @param  integer $len
21
	* @return void
22
	*/
23
	protected function adjustTextNodes($childType, $pos, $len = PHP_INT_MAX)
24
	{
25
		foreach ($this->getBranches() as $branch)
26
		{
27
			$node            = $branch->$childType;
28
			$node->nodeValue = substr($node->textContent, $pos, $len);
29
		}
30
	}
31
32
	/**
33
	* Compute the number of leading characters common to all strings
34
	*
35
	* @param  string[] $strings
36
	* @return integer
37
	*/
38
	protected function getPrefixLength(array $strings)
39
	{
40
		$i      = 0;
41
		$len    = 0;
42
		$maxLen = min(array_map('strlen', $strings));
43
		while ($i < $maxLen)
44
		{
45
			$c = $strings[0][$i];
46
			foreach ($strings as $string)
47
			{
48
				if ($string[$i] !== $c)
49
				{
50
					break 2;
51
				}
52
			}
53
			$len = ++$i;
54
		}
55
56
		return $len;
57
	}
58
59
	/**
60
	* Get the text content of the firstChild/lastChild of each branch if they are all text nodes
61
	*
62
	* @param  string   $childType Either firstChild or lastChild
63
	* @return string[]            List of strings or an empty array
64
	*/
65
	protected function getTextContent($childType)
66
	{
67
		$strings = [];
68
		foreach ($this->getBranches() as $branch)
69
		{
70
			if (!($branch->$childType instanceof DOMText))
71
			{
72
				return [];
73
			}
74
			$strings[] = $branch->$childType->textContent;
75
		}
76
77
		return $strings;
78
	}
79
80
	/**
81
	* {@inheritdoc}
82
	*/
83
	protected function optimizeChoose()
84
	{
85
		if (!$this->hasOtherwise())
86
		{
87
			return;
88
		}
89
90
		$this->optimizeLeadingText();
91
		$this->optimizeTrailingText();
92
	}
93
94
	/**
95
	* Move common leading text outside of current choose
96
	*
97
	* @return void
98
	*/
99
	protected function optimizeLeadingText()
100
	{
101
		$strings = $this->getTextContent('firstChild');
102
		if (empty($strings))
103
		{
104
			return;
105
		}
106
107
		$len = $this->getPrefixLength($strings);
108
		if ($len)
109
		{
110
			$this->adjustTextNodes('firstChild', $len);
111
			$this->choose->parentNode->insertBefore(
112
				$this->createTextNode(substr($strings[0], 0, $len)),
113
				$this->choose
114
			);
115
		}
116
	}
117
118
	/**
119
	* Move common trailing text outside of current choose
120
	*
121
	* @return void
122
	*/
123
	protected function optimizeTrailingText()
124
	{
125
		$strings = $this->getTextContent('lastChild');
126
		if (empty($strings))
127
		{
128
			return;
129
		}
130
131
		// Flip the strings before computing the prefix length to get the suffix length
132
		$len = $this->getPrefixLength(array_map('strrev', $strings));
133
		if ($len)
134
		{
135
			$this->adjustTextNodes('lastChild', 0, -$len);
136
			$this->choose->parentNode->insertBefore(
137
				$this->createTextNode(substr($strings[0], -$len)),
138
				$this->choose->nextSibling
139
			);
140
		}
141
	}
142
}