Test Failed
Pull Request — develop (#127)
by Sebastian
10:34 queued 05:19
created

Names::setRenderLabelBeforeName()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 2

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 0
cts 0
cp 0
crap 2
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\Name;
11
12
use Seboettg\CiteProc\CiteProc;
13
use Seboettg\CiteProc\Exception\CiteProcException;
14
use Seboettg\CiteProc\Exception\InvalidStylesheetException;
15
use Seboettg\CiteProc\Rendering\HasParent;
16
use Seboettg\CiteProc\Rendering\Label;
17
use Seboettg\CiteProc\Rendering\Rendering;
18
use Seboettg\CiteProc\Rendering\Term\Punctuation;
19
use Seboettg\CiteProc\RenderingState;
20
use Seboettg\CiteProc\Style\InheritableNameAttributesTrait;
21
use Seboettg\CiteProc\Styles\AffixesTrait;
22
use Seboettg\CiteProc\Styles\DelimiterTrait;
23
use Seboettg\CiteProc\Styles\FormattingTrait;
24
use Seboettg\CiteProc\Util\Factory;
25
use Seboettg\CiteProc\Util\NameHelper;
26
use Seboettg\Collection\ArrayList;
27
use SimpleXMLElement;
28
use stdClass;
29
30
/**
31
 * Class Names
32
 *
33
 * @package Seboettg\CiteProc\Rendering\Name
34
 *
35
 * @author Sebastian Böttger <[email protected]>
36
 */
37
class Names 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\Name\Names.
Loading history...
40
        AffixesTrait,
41
        FormattingTrait,
42
        InheritableNameAttributesTrait;
43
44
    /**
45
     * Variables (selected with the required variable attribute), each of which can contain multiple names (e.g. the
46
     * “author” variable contains all the author names of the cited item). If multiple variables are selected
47
     * (separated by single spaces, see example below), each variable is independently rendered in the order specified.
48
     *
49
     * @var ArrayList
50
     */
51
    private $variables;
52
53
    /**
54
     * The Name element, an optional child element of Names, can be used to describe the formatting of individual
55
     * names, and the separation of names within a name variable.
56
     *
57
     * @var Name
58
     */
59
    private $name;
60
61
    /**
62
     * The optional Label element must be included after the Name and EtAl elements, but before
63
     * the Substitute element. When used as a child element of Names, Label does not carry the variable
64
     * attribute; it uses the variable(s) set on the parent Names element instead.
65
     *
66
     * @var Label
67
     */
68
    private $label;
69
70
    /**
71
     * The optional Substitute element, which must be included as the last child element of Names, adds
72
     * substitution in case the name variables specified in the parent cs:names element are empty. The substitutions
73
     * are specified as child elements of Substitute, and must consist of one or more rendering elements (with the
74
     * exception of Layout). A shorthand version of Names without child elements, which inherits the attributes
75
     * values set on the cs:name and EtAl child elements of the original Names element, may also be used. If
76
     * Substitute contains multiple child elements, the first element to return a non-empty result is used for
77
     * substitution. Substituted variables are suppressed in the rest of the output to prevent duplication. An example,
78
     * where an empty “author” name variable is substituted by the “editor” name variable, or, when no editors exist,
79
     * by the “title” macro:
80
     *
81
     * <macro name="author">
82
     *     <names variable="author">
83
     *         <substitute>
84
     *             <names variable="editor"/>
85
     *             <text macro="title"/>
86
     *         </substitute>
87
     *     </names>
88
     * </macro>
89
     *
90
     * @var Substitute
91
     */
92
    private $substitute;
93
94
    /**
95
     * Et-al abbreviation, controlled via the et-al-... attributes (see Name), can be further customized with the
96
     * optional cs:et-al element, which must follow the cs:name element (if present). The term attribute may be set to
97
     * either “et-al” (the default) or to “and others” to use either term. The formatting attributes may also be used,
98
     * for example to italicize the “et-al” term:
99
     *
100
     * @var EtAl
101
     */
102
    private $etAl;
103
104
    /**
105
     * The delimiter attribute may be set on cs:names to separate the names of the different name variables (e.g. the
106
     * semicolon in “Doe, Smith (editors); Johnson (translator)”).
107
     *
108
     * @var string
109
     */
110
    private $delimiter = ", ";
111
112
    private $parent;
113
114
    /**
115
     * @var bool
116
     */
117
    private $renderLabelBeforeName = false;
118
119
    /**
120 119
     * Names constructor.
121
     *
122 119
     * @param  SimpleXMLElement $node
123 119
     * @param  $parent
124
     * @throws InvalidStylesheetException
125
     */
126
    public function __construct(SimpleXMLElement $node, $parent)
127 119
    {
128 119
        $this->initInheritableNameAttributes($node);
129 119
        $this->parent = $parent;
130 119
        /**
131 119
         * @var SimpleXMLElement $child
132 63
         */
133 57
134 57
        foreach ($node->children() as $child) {
135 58
            switch ($child->getName()) {
136 57
                case "name":
137 57
                    $this->name = Factory::create($child, $this);
138 13
                    if ($this->label !== null) {
139 13
                        $this->renderLabelBeforeName = true;
140
                    }
141
                    break;
142
                case "label":
143
                    $this->label = Factory::create($child);
144
                    break;
145
                case "substitute":
146 119
                    $this->substitute = new Substitute($child, $this);
0 ignored issues
show
Bug introduced by
It seems like $child can also be of type null; however, parameter $node of Seboettg\CiteProc\Render...bstitute::__construct() does only seem to accept SimpleXMLElement, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

146
                    $this->substitute = new Substitute(/** @scrutinizer ignore-type */ $child, $this);
Loading history...
147 119
                    break;
148 119
                case "et-al":
149 119
                    $this->etAl = Factory::create($child);
150
            }
151
        }
152
153 119
        /**
154 119
         * @var SimpleXMLElement $attribute
155 119
         */
156 119
        foreach ($node->attributes() as $attribute) {
157
            if ("variable" === $attribute->getName()) {
158
                $this->variables = new ArrayList(...explode(" ", (string) $attribute));
159
                break;
160
            }
161
        }
162
163
        $this->initDelimiterAttributes($node);
164
        $this->initAffixesAttributes($node);
165
        $this->initFormattingAttributes($node);
166
    }
167
168
    /**
169
     * This outputs the contents of one or more name variables (selected with the required variable attribute), each
170
     * of which can contain multiple names (e.g. the “author” variable contains all the author names of the cited item).
171
     * If multiple variables are selected (separated by single spaces), each variable is independently rendered in the
172 113
     * order specified, with one exception: when the selection consists of “editor” and “translator”, and when the
173
     * contents of these two name variables is identical, then the contents of only one name variable is rendered. In
174 113
     * addition, the “editortranslator” term is used if the Names element contains a Label element, replacing the
175
     * default “editor” and “translator” terms (e.g. resulting in “Doe (editor & translator)”).
176
     *
177
     * @param  stdClass $data
178
     * @param  int|null $citationNumber
179
     * @return string
180 113
     * @throws CiteProcException
181 16
     */
182 16
    public function render($data, $citationNumber = null)
183
    {
184 1
        $str = "";
185 1
186
        /* when the selection consists of “editor” and “translator”, and when the contents of these two name variables
187
        is identical, then the contents of only one name variable is rendered. In addition, the “editortranslator”
188
        term is used if the cs:names element contains a cs:label element, replacing the default “editor” and
189
        “translator” terms (e.g. resulting in “Doe (editor & translator)”) */
190
        if ($this->variables->hasElement("editor") && $this->variables->hasElement("translator")) {
191
            if (isset($data->editor)
192
                && isset($data->translator) && NameHelper::sameNames($data->editor, $data->translator)
193
            ) {
194 1
                if (isset($this->name)) {
195 1
                    $str .= $this->name->render($data, 'editor');
196 1
                } else {
197
                    $arr = [];
198 1
                    foreach ($data->editor as $editor) {
199
                        $edt = $this->format($editor->family.", ".$editor->given);
200 1
                        $results[] = NameHelper::addExtendedMarkup('editor', $editor, $edt);
201 1
                    }
202 1
                    $str .= implode($this->delimiter, $arr);
203
                }
204
                if (isset($this->label)) {
205
                    $this->label->setVariable("editortranslator");
206 113
                    $str .= $this->label->render($data);
207 113
                }
208 112
                $vars = $this->variables->toArray();
209 104
                $vars = array_filter($vars, function ($value) {
210 103
                    return !($value === "editor" || $value === "translator");
211 103
                });
212 103
                $this->variables->setArray($vars);
213 33
            }
214
        }
215
216 103
        $results = [];
217 6
        foreach ($this->variables as $var) {
218
            if (!empty($data->{$var})) {
219 103
                if (!empty($this->name)) {
220
                    $res = $this->name->render($data, $var, $citationNumber);
221
                    $name = $res;
222 1
                    if (!empty($this->label)) {
223 1
                        $name = $this->appendLabel($data, $var, $name);
224 1
                    }
225
                    //add multiple counting values
226
                    if (is_numeric($name) && $this->name->getForm() === "count") {
227
                        $results = $this->addCountValues($res, $results);
228 104
                    } else {
229 104
                        $results[] = $this->format($name);
230
                    }
231
                } else {
232 37
                    foreach ($data->{$var} as $name) {
233 22
                        $formatted = $this->format($name->given." ".$name->family);
234
                        $results[] = NameHelper::addExtendedMarkup($var, $name, $formatted);
235
                    }
236
                }
237 113
                // suppress substituted variables
238 113
                if (CiteProc::getContext()->getRenderingState()->getValue() === RenderingState::SUBSTITUTION) {
239 113
                    unset($data->{$var});
240
                }
241
            } else {
242
                if (!empty($this->substitute)) {
243
                    $results[] = $this->substitute->render($data);
244
                }
245
            }
246
        }
247
        $results = $this->filterEmpty($results);
248
        $str .= implode($this->delimiter, $results);
249 33
        return !empty($str) ? $this->addAffixes($str) : "";
250
    }
251 33
252 33
253 1
    /**
254
     * @param  $data
255 33
     * @param  $var
256
     * @param  $name
257 33
     * @return string
258
     */
259
    private function appendLabel($data, $var, $name): string
260
    {
261
        $this->label->setVariable($var);
262
        $renderedLabel = trim($this->label->render($data));
263
        if (empty($renderedLabel)) {
264
            return $name;
265 6
        }
266
        if ($this->renderLabelBeforeName) {
267 6
            $delimiter = !in_array(
268 6
                trim($this->label->renderSuffix()),
269 6
                Punctuation::getAllPunctuations()
270 3
            ) ? " " : "";
271 3
            $result = $renderedLabel . $delimiter . trim($name);
272
        } else {
273 6
            $delimiter = !in_array(
274
                trim($this->label->renderPrefix()),
275 6
                Punctuation::getAllPunctuations()
276
            ) ? " " : "";
277
            $result = trim($name) . $delimiter . $renderedLabel;
278
        }
279
        return $result;
280
    }
281 71
282
    /**
283 71
     * @param  $res
284
     * @param  $results
285
     * @return array
286
     */
287
    private function addCountValues($res, $results)
288
    {
289 12
        $lastElement = current($results);
290
        $key = key($results);
291 12
        if (!empty($lastElement)) {
292
            $lastElement += $res;
293
            $results[$key] = $lastElement;
294
        } else {
295
            $results[] = $res;
296
        }
297
        return $results;
298 12
    }
299
300 12
    /**
301 12
     * @return bool
302
     */
303
    public function hasEtAl()
304
    {
305
        return !empty($this->etAl);
306
    }
307 56
308
    /**
309 56
     * @return EtAl
310
     */
311
    public function getEtAl()
312
    {
313
        return $this->etAl;
314
    }
315 56
316
    /**
317 56
     * @param  EtAl $etAl
318
     * @return $this
319
     */
320
    public function setEtAl(EtAl $etAl)
321
    {
322
        $this->etAl = $etAl;
323
        return $this;
324 56
    }
325
326 56
    /**
327 56
     * @return bool
328
     */
329
    public function hasName()
330
    {
331
        return !empty($this->name);
332
    }
333 97
334
    /**
335 97
     * @return Name
336
     */
337
    public function getName()
338
    {
339
        return $this->name;
340
    }
341 104
342
    /**
343 104
     * @param  Name $name
344
     * @return $this
345
     */
346
    public function setName(Name $name)
347
    {
348
        $this->name = $name;
349 56
        return $this;
350
    }
351 56
352
    /**
353
     * @return string
354
     */
355
    public function getDelimiter()
356
    {
357 48
        return $this->delimiter;
358
    }
359 48
360
    /**
361
     * @return ArrayList
362
     */
363
    public function getVariables()
364
    {
365 48
        return $this->variables;
366
    }
367 48
368 48
    /**
369
     * @return bool
370
     */
371
    public function hasLabel()
372
    {
373 63
        return !empty($this->label);
374
    }
375 63
376
    /**
377
     * @return Label
378 113
     */
379
    public function getLabel()
380
    {
381 112
        return $this->label;
382 113
    }
383
384
    /**
385
     * @param Label $label
386
     */
387
    public function setLabel($label)
388
    {
389
        $this->label = $label;
390
    }
391
392
    /**
393
     * @return mixed
394
     */
395
    public function getParent()
396
    {
397
        return $this->parent;
398
    }
399
400
    private function filterEmpty(array $results)
401
    {
402
        return array_filter($results, function ($item) {
403
            return !empty($item);
404
        });
405
    }
406
407
    /**
408
     * @return bool
409
     */
410
    public function isRenderLabelBeforeName(): bool
411
    {
412
        return $this->renderLabelBeforeName;
413
    }
414
415
    /**
416
     * @param bool $renderLabelBeforeName
417
     */
418
    public function setRenderLabelBeforeName(bool $renderLabelBeforeName): void
419
    {
420
        $this->renderLabelBeforeName = $renderLabelBeforeName;
421
    }
422
}
423