Group::isSourceVariable()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 1
nc 2
nop 1
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/*
3
 * citeproc-php
4
 *
5
 * @link        http://github.com/seboettg/citeproc-php for the source repository
6
 * @copyright   Copyright (c) 2016 Sebastian Böttger.
7
 * @license     https://opensource.org/licenses/MIT
8
 */
9
10
namespace Seboettg\CiteProc\Rendering;
11
12
use Seboettg\CiteProc\Exception\InvalidStylesheetException;
13
use Seboettg\CiteProc\Styles\AffixesTrait;
14
use Seboettg\CiteProc\Styles\ConsecutivePunctuationCharacterTrait;
15
use Seboettg\CiteProc\Styles\DelimiterTrait;
16
use Seboettg\CiteProc\Styles\DisplayTrait;
17
use Seboettg\CiteProc\Styles\FormattingTrait;
18
use Seboettg\CiteProc\Util\Factory;
19
use Seboettg\CiteProc\Util\StringHelper;
20
use Seboettg\Collection\ArrayList;
21
use SimpleXMLElement;
22
use function Seboettg\Collection\Lists\emptyList;
23
24
/**
25
 * Class Group
26
 * The cs:group rendering element must contain one or more rendering elements (with the exception of cs:layout).
27
 * cs:group may carry the delimiter attribute to separate its child elements, as well as affixes and display attributes
28
 * (applied to the output of the group as a whole) and formatting attributes (transmitted to the enclosed elements).
29
 * cs:group implicitly acts as a conditional: cs:group and its child elements are suppressed if a) at least one
30
 * rendering element in cs:group calls a variable (either directly or via a macro), and b) all variables that are
31
 * called are empty. This accommodates descriptive cs:text elements.
32
 *
33
 * @package Seboettg\CiteProc\Rendering
34
 *
35
 * @author Sebastian Böttger <[email protected]>
36
 */
37
class Group implements Rendering, HasParent
38
{
39
    use DelimiterTrait,
0 ignored issues
show
Bug introduced by
The trait Seboettg\CiteProc\Styles\AffixesTrait requires the property $single which is not provided by Seboettg\CiteProc\Rendering\Group.
Loading history...
40
        AffixesTrait,
41
        DisplayTrait,
42
        FormattingTrait,
43
        ConsecutivePunctuationCharacterTrait;
44
45
    /**
46
     * @var ArrayList
47
     */
48
    private $children;
49
50
    /**
51
     * cs:group may carry the delimiter attribute to separate its child elements
52
     *
53
     * @var
54
     */
55
    private $delimiter = "";
56
57
    private $parent;
58
59
    /**
60
     * @var array
61
     */
62
    private $renderedChildsWithVariable = [];
63
64
65
    /**
66
     * Group constructor.
67
     *
68
     * @param SimpleXMLElement $node
69
     * @param  $parent
70
     * @throws InvalidStylesheetException
71
     */
72
    public function __construct(SimpleXMLElement $node, $parent)
73
    {
74
        $this->parent = $parent;
75
        $this->children = emptyList();
0 ignored issues
show
Documentation Bug introduced by
It seems like emptyList() of type anonymous//vendor/seboet...c/Lists/Functions.php$0 is incompatible with the declared type Seboettg\Collection\ArrayList of property $children.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
76
        foreach ($node->children() as $child) {
77
            $this->children->add(Factory::create($child, $this));
78
        }
79
        $this->initDisplayAttributes($node);
80
        $this->initAffixesAttributes($node);
81
        $this->initDelimiterAttributes($node);
82
        $this->initFormattingAttributes($node);
83
    }
84
85
    /**
86
     * @param  $data
87
     * @param int|null $citationNumber
88
     * @return string
89
     */
90
    public function render($data, $citationNumber = null)
91
    {
92
        $textParts = [];
93
        $terms = $variables = $haveVariables = $elementCount = 0;
94
        foreach ($this->children as $child) {
95
            $elementCount++;
96
97
            // cs:group implicitly acts as a conditional: cs:group and its child elements are suppressed if
98
            // a) at least one rendering element in cs:group calls a variable (either directly or via a macro), and
99
            // b) all variables that are called are empty. This accommodates descriptive cs:text and `cs:label`elements.
100
            if ($this->isChildATerm($child)) {
101
                ++$terms;
102
            }
103
104
            if ($this->isChildAVariable($child)) {
105
                ++$variables;
106
            }
107
108
            $text = $child->render($data, $citationNumber);
109
            if (!empty($text)) {
110
                /*
111
                if ($delimiter && ($elementCount < count($this->children))) {
112
                    //check to see if the delimiter is already the last character of the text string
113
                    //if so, remove it. So we don't have two of them when the group will be merged
114
                    $stext = strip_tags(trim($text));
115
                    if ((strrpos($stext, $delimiter[0]) + 1) == strlen($stext) && strlen($stext) > 1) {
116
                        $text = str_replace($stext, '----REPLACE----', $text);
117
                        $stext = substr($stext, 0, -1);
118
                        $text = str_replace('----REPLACE----', $stext, $text);
119
                    }
120
                }
121
                */
122
                $textParts[] = $text;
123
124
                if ($this->isSourceVariable($child) || $this->isSourceVariableButNoDate($child)) {
125
                    $haveVariables++;
126
                }
127
128
                if ($this->isSourceMacro($child)) {
129
                    $haveVariables++;
130
                }
131
            }
132
        }
133
        return $this->formatting($textParts, $variables, $haveVariables, $terms);
134
    }
135
136
    public function getParent()
137
    {
138
        return $this->parent;
139
    }
140
141
    /**
142
     * @param  $textParts
143
     * @param  $variables
144
     * @param  $haveVariables
145
     * @param  $terms
146
     * @return string
147
     */
148
    protected function formatting($textParts, $variables, $haveVariables, $terms)
149
    {
150
        if (empty($textParts)) {
151
            return "";
152
        }
153
154
        if ($variables && !$haveVariables) {
155
            return ""; // there has to be at least one other none empty value before the term is output
156
        }
157
158
        if (count($textParts) == $terms) {
159
            return ""; // there has to be at least one other none empty value before the term is output
160
        }
161
162
        $text = StringHelper::implodeAndPreventConsecutiveChars($this->delimiter, $textParts);
163
164
        if (!empty($text)) {
165
            return $this->wrapDisplayBlock($this->addAffixes($this->format(($text))));
166
        }
167
168
        return "";
169
    }
170
171
    /**
172
     * @return bool
173
     */
174
    public function hasDelimiter()
175
    {
176
        return !empty($this->delimiter);
177
    }
178
179
    /**
180
     * @return string
181
     */
182
    public function getDelimiter()
183
    {
184
        return $this->delimiter;
185
    }
186
187
    private function isChildATerm($child): bool
188
    {
189
        return ($child instanceof Text) && ($child->getSource() == 'term' || $child->getSource() == 'value')
190
            || ($child instanceof Label);
191
    }
192
193
    private function isChildAVariable($child): bool
194
    {
195
        return $this->isSourceVariable($child) && $this->isSourceVariableButNoDate($child)
196
            && !empty($data->{$child->getVariable()});
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $data seems to be never defined.
Loading history...
197
    }
198
199
200
    private function isSourceVariable($child): bool
201
    {
202
        return method_exists($child, "getSource") && $child->getSource() == 'variable';
203
    }
204
205
    private function isSourceVariableButNoDate($child): bool
206
    {
207
        return method_exists($child, "getVariable") &&
208
            $child->getVariable() !== "date" && !empty($child->getVariable());
209
    }
210
211
    private function isSourceMacro($child): bool
212
    {
213
        return method_exists($child, "getSource") && $child->getSource() == 'macro';
214
    }
215
}
216