Passed
Push — develop ( e162a8...209be3 )
by Sebastian
14:13 queued 09:03
created

Names::setLabel()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
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
     * Names constructor.
121
     *
122
     * @param  SimpleXMLElement $node
123
     * @param  $parent
124
     * @throws InvalidStylesheetException
125
     */
126 125
    public function __construct(SimpleXMLElement $node, $parent)
127
    {
128 125
        $this->initInheritableNameAttributes($node);
129 125
        $this->parent = $parent;
130
        /**
131
         * @var SimpleXMLElement $child
132
         */
133
134 125
        foreach ($node->children() as $child) {
135 125
            switch ($child->getName()) {
136 125
                case "name":
137 125
                    $this->name = Factory::create($child, $this);
138 125
                    if ($this->label !== null) {
139 34
                        $this->renderLabelBeforeName = true;
140
                    }
141 125
                    break;
142 69
                case "label":
143 63
                    $this->label = Factory::create($child);
144 63
                    break;
145 60
                case "substitute":
146 59
                    $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 59
                    break;
148 13
                case "et-al":
149 13
                    $this->etAl = Factory::create($child);
150
            }
151
        }
152
153
        /**
154
         * @var SimpleXMLElement $attribute
155
         */
156 125
        foreach ($node->attributes() as $attribute) {
157 125
            if ("variable" === $attribute->getName()) {
158 125
                $this->variables = new ArrayList(...explode(" ", (string) $attribute));
159 125
                break;
160
            }
161
        }
162
163 125
        $this->initDelimiterAttributes($node);
164 125
        $this->initAffixesAttributes($node);
165 125
        $this->initFormattingAttributes($node);
166 125
    }
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
     * 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
     * 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
     * @throws CiteProcException
181
     */
182 117
    public function render($data, $citationNumber = null)
183
    {
184 117
        $str = "";
185
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 117
        if ($this->variables->hasElement("editor") && $this->variables->hasElement("translator")) {
191 18
            if (isset($data->editor)
192 18
                && 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
                } else {
197
                    $arr = [];
198
                    foreach ($data->editor as $editor) {
199
                        $edt = $this->format($editor->family.", ".$editor->given);
200
                        $results[] = NameHelper::addExtendedMarkup('editor', $editor, $edt);
201
                    }
202
                    $str .= implode($this->delimiter, $arr);
203
                }
204 1
                if (isset($this->label)) {
205 1
                    $this->label->setVariable("editortranslator");
206 1
                    $str .= $this->label->render($data);
207
                }
208 1
                $vars = $this->variables->toArray();
209
                $vars = array_filter($vars, function ($value) {
210 1
                    return !($value === "editor" || $value === "translator");
211 1
                });
212 1
                $this->variables->setArray($vars);
213
            }
214
        }
215
216 117
        $results = [];
217 117
        foreach ($this->variables as $var) {
218 116
            if (!empty($data->{$var})) {
219 108
                if (!empty($this->name)) {
220 107
                    $res = $this->name->render($data, $var, $citationNumber);
221 107
                    $name = $res;
222 107
                    if (!empty($this->label)) {
223 37
                        $name = $this->appendLabel($data, $var, $name);
224
                    }
225
                    //add multiple counting values
226 107
                    if (is_numeric($name) && $this->name->getForm() === "count") {
227 6
                        $results = $this->addCountValues($res, $results);
228
                    } else {
229 107
                        $results[] = $this->format($name);
230
                    }
231
                } else {
232 1
                    foreach ($data->{$var} as $name) {
233 1
                        $formatted = $this->format($name->given." ".$name->family);
234 1
                        $results[] = NameHelper::addExtendedMarkup($var, $name, $formatted);
235
                    }
236
                }
237
                // suppress substituted variables
238 108
                if (CiteProc::getContext()->getRenderingState()->getValue() === RenderingState::SUBSTITUTION) {
239 108
                    unset($data->{$var});
240
                }
241
            } else {
242 39
                if (!empty($this->substitute)) {
243 22
                    $results[] = $this->substitute->render($data);
244
                }
245
            }
246
        }
247 117
        $results = $this->filterEmpty($results);
248 117
        $str .= implode($this->delimiter, $results);
249 117
        return !empty($str) ? $this->addAffixes($str) : "";
250
    }
251
252
253
    /**
254
     * @param  $data
255
     * @param  $var
256
     * @param  $name
257
     * @return string
258
     */
259 37
    private function appendLabel($data, $var, $name): string
260
    {
261 37
        $this->label->setVariable($var);
262 37
        $renderedLabel = trim($this->label->render($data));
263 37
        if (empty($renderedLabel)) {
264 31
            return $name;
265
        }
266 9
        if ($this->renderLabelBeforeName) {
267 3
            $delimiter = !in_array(
268 3
                trim($this->label->renderSuffix()),
269 3
                Punctuation::getAllPunctuations()
270 3
            ) ? " " : "";
271 3
            $result = $renderedLabel . $delimiter . trim($name);
272
        } else {
273 6
            $delimiter = !in_array(
274 6
                trim($this->label->renderPrefix()),
275 6
                Punctuation::getAllPunctuations()
276 6
            ) ? " " : "";
277 6
            $result = trim($name) . $delimiter . $renderedLabel;
278
        }
279 9
        return $result;
280
    }
281
282
    /**
283
     * @param  $res
284
     * @param  $results
285
     * @return array
286
     */
287 6
    private function addCountValues($res, $results)
288
    {
289 6
        $lastElement = current($results);
290 6
        $key = key($results);
291 6
        if (!empty($lastElement)) {
292 3
            $lastElement += $res;
293 3
            $results[$key] = $lastElement;
294
        } else {
295 6
            $results[] = $res;
296
        }
297 6
        return $results;
298
    }
299
300
    /**
301
     * @return bool
302
     */
303 73
    public function hasEtAl()
304
    {
305 73
        return !empty($this->etAl);
306
    }
307
308
    /**
309
     * @return EtAl
310
     */
311 12
    public function getEtAl()
312
    {
313 12
        return $this->etAl;
314
    }
315
316
    /**
317
     * @param  EtAl $etAl
318
     * @return $this
319
     */
320 12
    public function setEtAl(EtAl $etAl)
321
    {
322 12
        $this->etAl = $etAl;
323 12
        return $this;
324
    }
325
326
    /**
327
     * @return bool
328
     */
329 58
    public function hasName()
330
    {
331 58
        return !empty($this->name);
332
    }
333
334
    /**
335
     * @return Name
336
     */
337 58
    public function getName()
338
    {
339 58
        return $this->name;
340
    }
341
342
    /**
343
     * @param  Name $name
344
     * @return $this
345
     */
346 58
    public function setName(Name $name)
347
    {
348 58
        $this->name = $name;
349 58
        return $this;
350
    }
351
352
    /**
353
     * @return string
354
     */
355 99
    public function getDelimiter()
356
    {
357 99
        return $this->delimiter;
358
    }
359
360
    /**
361
     * @return ArrayList
362
     */
363 108
    public function getVariables()
364
    {
365 108
        return $this->variables;
366
    }
367
368
    /**
369
     * @return bool
370
     */
371 58
    public function hasLabel()
372
    {
373 58
        return !empty($this->label);
374
    }
375
376
    /**
377
     * @return Label
378
     */
379 50
    public function getLabel()
380
    {
381 50
        return $this->label;
382
    }
383
384
    /**
385
     * @param Label $label
386
     */
387 50
    public function setLabel($label)
388
    {
389 50
        $this->label = $label;
390 50
    }
391
392
    /**
393
     * @return mixed
394
     */
395 65
    public function getParent()
396
    {
397 65
        return $this->parent;
398
    }
399
400 117
    private function filterEmpty(array $results)
401
    {
402
        return array_filter($results, function ($item) {
403 116
            return !empty($item);
404 117
        });
405
    }
406
407
    /**
408
     * @return bool
409
     */
410 2
    public function isRenderLabelBeforeName(): bool
411
    {
412 2
        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