Completed
Branch TemplateNormalizations (ae42e4)
by Josh
33:16
created

InlineInferredValues::normalizeElement()   A

Complexity

Conditions 4
Paths 2

Size

Total Lines 13
Code Lines 7

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
dl 0
loc 13
rs 9.2
c 0
b 0
f 0
cc 4
eloc 7
nc 2
nop 1
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 DOMAttr;
11
use DOMElement;
12
use DOMNode;
13
use s9e\TextFormatter\Configurator\Helpers\AVTHelper;
14
use s9e\TextFormatter\Configurator\Helpers\TemplateParser;
15
16
class InlineInferredValues extends AbstractNormalization
17
{
18
	/**
19
	* {@inheritdoc}
20
	*/
21
	protected $queries = ['//xsl:if', '//xsl:when'];
22
23
	/**
24
	* Inline the text content of a node or the value of an attribute where it's known
25
	*
26
	* Will replace
27
	*     <xsl:if test="@foo='Foo'"><xsl:value-of select="@foo"/></xsl:if>
28
	* with
29
	*     <xsl:if test="@foo='Foo'">Foo</xsl:if>
30
	*
31
	* @param  DOMElement $element
32
	* @return void
33
	*/
34
	protected function normalizeElement(DOMElement $element)
35
	{
36
		// Test whether the map has exactly one key and one value
37
		$map = TemplateParser::parseEqualityExpr($element->getAttribute('test'));
38
		if ($map === false || count($map) !== 1 || count($map[key($map)]) !== 1)
39
		{
40
			return;
41
		}
42
43
		$expr  = key($map);
44
		$value = end($map[$expr]);
45
		$this->inlineInferredValue($element, $expr, $value);
46
	}
47
48
	/**
49
	* Replace the inferred value in given node and its descendants
50
	*
51
	* @param  DOMNode $node  Context node
52
	* @param  string  $expr  XPath expression
53
	* @param  string  $value Inferred value
54
	* @return void
55
	*/
56
	protected function inlineInferredValue(DOMNode $node, $expr, $value)
57
	{
58
		// Get xsl:value-of descendants that match the condition
59
		$query = './/xsl:value-of[@select="' . $expr . '"]';
60
		foreach ($this->xpath($query, $node) as $valueOf)
61
		{
62
			$this->replaceValueOf($valueOf, $value);
63
		}
64
65
		// Get all attributes from non-XSL elements that *could* match the condition
66
		$query = './/*[namespace-uri() != $XSL]/@*[contains(., "{' . $expr . '}")]';
67
		foreach ($this->xpath($query, $node) as $attribute)
68
		{
69
			$this->replaceAttribute($attribute, $expr, $value);
70
		}
71
	}
72
73
	/**
74
	* Replace an expression with a literal value in given attribute
75
	*
76
	* @param  DOMAttr $attribute
77
	* @param  string  $expr
78
	* @param  string  $value
79
	* @return void
80
	*/
81
	protected function replaceAttribute(DOMAttr $attribute, $expr, $value)
82
	{
83
		AVTHelper::replace(
84
			$attribute,
85
			function ($token) use ($expr, $value)
86
			{
87
				// Test whether this expression is the one we're looking for
88
				if ($token[0] === 'expression' && $token[1] === $expr)
89
				{
90
					// Replace the expression with the value (as a literal)
91
					$token = ['literal', $value];
92
				}
93
94
				return $token;
95
			}
96
		);
97
	}
98
99
	/**
100
	* Replace an xsl:value-of element with a literal value
101
	*
102
	* @param  DOMElement $valueOf
103
	* @param  string     $value
104
	* @return void
105
	*/
106
	protected function replaceValueOf(DOMElement $valueOf, $value)
107
	{
108
		$valueOf->parentNode->replaceChild(
109
			$valueOf->ownerDocument->createTextNode($value),
110
			$valueOf
111
		);
112
	}
113
}