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

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