Passed
Push — feature/116-csif-not-working-i... ( 4a0df2 )
by Sebastian
02:26 queued 13s
created

Names::isRenderLabelBeforeName()   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 0
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 124
    public function __construct(SimpleXMLElement $node, $parent)
127
    {
128 124
        $this->initInheritableNameAttributes($node);
129 124
        $this->parent = $parent;
130
        /**
131
         * @var SimpleXMLElement $child
132
         */
133
134 124
        foreach ($node->children() as $child) {
135 124
            switch ($child->getName()) {
136 124
                case "name":
137 124
                    $this->name = Factory::create($child, $this);
138 124
                    if ($this->label !== null) {
139 33
                        $this->renderLabelBeforeName = true;
140
                    }
141 124
                    break;
142 68
                case "label":
143 62
                    $this->label = Factory::create($child);
144 62
                    break;
145 59
                case "substitute":
146 58
                    $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 58
                    break;
148 13
                case "et-al":
149 13
                    $this->etAl = Factory::create($child);
150
            }
151
        }
152
153
        /**
154
         * @var SimpleXMLElement $attribute
155
         */
156 124
        foreach ($node->attributes() as $attribute) {
157 124
            if ("variable" === $attribute->getName()) {
158 124
                $this->variables = new ArrayList(...explode(" ", (string) $attribute));
159 124
                break;
160
            }
161
        }
162
163 124
        $this->initDelimiterAttributes($node);
164 124
        $this->initAffixesAttributes($node);
165 124
        $this->initFormattingAttributes($node);
166 124
    }
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 116
    public function render($data, $citationNumber = null)
183
    {
184 116
        $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 116
        if ($this->variables->hasElement("editor") && $this->variables->hasElement("translator")) {
191 17
            if (isset($data->editor)
192 17
                && 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 116
        $results = [];
217 116
        foreach ($this->variables as $var) {
218 115
            if (!empty($data->{$var})) {
219 107
                if (!empty($this->name)) {
220 106
                    $res = $this->name->render($data, $var, $citationNumber);
221 106
                    $name = $res;
222 106
                    if (!empty($this->label)) {
223 36
                        $name = $this->appendLabel($data, $var, $name);
224
                    }
225
                    //add multiple counting values
226 106
                    if (is_numeric($name) && $this->name->getForm() === "count") {
227 6
                        $results = $this->addCountValues($res, $results);
228
                    } else {
229 106
                        $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 107
                if (CiteProc::getContext()->getRenderingState()->getValue() === RenderingState::SUBSTITUTION) {
239 107
                    unset($data->{$var});
240
                }
241
            } else {
242 38
                if (!empty($this->substitute)) {
243 22
                    $results[] = $this->substitute->render($data);
244
                }
245
            }
246
        }
247 116
        $results = $this->filterEmpty($results);
248 116
        $str .= implode($this->delimiter, $results);
249 116
        return !empty($str) ? $this->addAffixes($str) : "";
250
    }
251
252
253
    /**
254
     * @param  $data
255
     * @param  $var
256
     * @param  $name
257
     * @return string
258
     */
259 36
    private function appendLabel($data, $var, $name): string
260
    {
261 36
        $this->label->setVariable($var);
262 36
        $renderedLabel = trim($this->label->render($data));
263 36
        if (empty($renderedLabel)) {
264 31
            return $name;
265
        }
266 8
        if ($this->renderLabelBeforeName) {
267 2
            $delimiter = !in_array(
268 2
                trim($this->label->renderSuffix()),
269 2
                Punctuation::getAllPunctuations()
270 2
            ) ? " " : "";
271 2
            $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 8
        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 72
    public function hasEtAl()
304
    {
305 72
        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 57
    public function hasName()
330
    {
331 57
        return !empty($this->name);
332
    }
333
334
    /**
335
     * @return Name
336
     */
337 57
    public function getName()
338
    {
339 57
        return $this->name;
340
    }
341
342
    /**
343
     * @param  Name $name
344
     * @return $this
345
     */
346 57
    public function setName(Name $name)
347
    {
348 57
        $this->name = $name;
349 57
        return $this;
350
    }
351
352
    /**
353
     * @return string
354
     */
355 98
    public function getDelimiter()
356
    {
357 98
        return $this->delimiter;
358
    }
359
360
    /**
361
     * @return ArrayList
362
     */
363 107
    public function getVariables()
364
    {
365 107
        return $this->variables;
366
    }
367
368
    /**
369
     * @return bool
370
     */
371 57
    public function hasLabel()
372
    {
373 57
        return !empty($this->label);
374
    }
375
376
    /**
377
     * @return Label
378
     */
379 49
    public function getLabel()
380
    {
381 49
        return $this->label;
382
    }
383
384
    /**
385
     * @param Label $label
386
     */
387 49
    public function setLabel($label)
388
    {
389 49
        $this->label = $label;
390 49
    }
391
392
    /**
393
     * @return mixed
394
     */
395 64
    public function getParent()
396
    {
397 64
        return $this->parent;
398
    }
399
400 116
    private function filterEmpty(array $results)
401
    {
402
        return array_filter($results, function ($item) {
403 115
            return !empty($item);
404 116
        });
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