Passed
Push — new-api ( 5677f6...2a03a6 )
by Sebastian
04:12
created

Names::getParent()   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 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 1
c 1
b 0
f 0
nc 1
nop 0
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
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\Label;
17
use Seboettg\CiteProc\Rendering\Rendering;
18
use Seboettg\CiteProc\Config\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
     * Names constructor.
115
     *
116
     * @param  SimpleXMLElement $node
117
     * @param  $parent
118
     * @throws InvalidStylesheetException
119
     */
120 116
    public function __construct(SimpleXMLElement $node, $parent = null)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$parent" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$parent"; expected 0 but found 1
Loading history...
121
    {
122 116
        $this->initInheritableNameAttributes($node);
123 116
        $this->parent = $parent;
124
        /**
125
         * @var SimpleXMLElement $child
126
         */
127 116
        foreach ($node->children() as $child) {
128 116
            switch ($child->getName()) {
129 116
                case "name":
130 116
                    $this->name = Factory::create($child, $this);
131 116
                    break;
132 60
                case "label":
133 54
                    $this->label = Factory::create($child);
134 54
                    break;
135 55
                case "substitute":
136 54
                    $this->substitute = new Substitute($child, $this);
137 54
                    break;
138 13
                case "et-al":
139 116
                    $this->etAl = Factory::create($child);
140
            }
141
        }
142
143 116
        foreach ($node->attributes() as $attribute) {
144 116
            if ("variable" === $attribute->getName()) {
145 116
                $this->variables = new ArrayList(...explode(" ", (string) $attribute));
146 116
                break;
147
            }
148
        }
149
150 116
        $this->initDelimiterAttributes($node);
151 116
        $this->initAffixesAttributes($node);
152 116
        $this->initFormattingAttributes($node);
153 116
    }
154
155
    /**
156
     * This outputs the contents of one or more name variables (selected with the required variable attribute), each
157
     * of which can contain multiple names (e.g. the “author” variable contains all the author names of the cited item).
158
     * If multiple variables are selected (separated by single spaces), each variable is independently rendered in the
159
     * order specified, with one exception: when the selection consists of “editor” and “translator”, and when the
160
     * contents of these two name variables is identical, then the contents of only one name variable is rendered. In
161
     * addition, the “editortranslator” term is used if the Names element contains a Label element, replacing the
162
     * default “editor” and “translator” terms (e.g. resulting in “Doe (editor & translator)”).
163
     *
164
     * @param  stdClass $data
165
     * @param  int|null $citationNumber
166
     * @return string
167
     * @throws CiteProcException
168
     */
169 110
    public function render($data, $citationNumber = null)
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$citationNumber" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$citationNumber"; expected 0 but found 1
Loading history...
170
    {
171 110
        $str = "";
172
173
        /* when the selection consists of “editor” and “translator”, and when the contents of these two name variables
174
        is identical, then the contents of only one name variable is rendered. In addition, the “editortranslator”
175
        term is used if the cs:names element contains a cs:label element, replacing the default “editor” and
176
        “translator” terms (e.g. resulting in “Doe (editor & translator)”) */
177 110
        if ($this->variables->hasElement("editor") && $this->variables->hasElement("translator")) {
178 15
            if (isset($data->editor)
179 15
                && isset($data->translator) && NameHelper::sameNames($data->editor, $data->translator)
180
            ) {
181 1
                if (isset($this->name)) {
182 1
                    $str .= $this->name->render($data, 'editor');
183
                } else {
184
                    $arr = [];
185
                    foreach ($data->editor as $editor) {
186
                        $edt = $this->format($editor->family.", ".$editor->given);
187
                        $results[] = NameHelper::addExtendedMarkup('editor', $editor, $edt);
188
                    }
189
                    $str .= implode($this->delimiter, $arr);
190
                }
191 1
                if (isset($this->label)) {
192 1
                    $this->label->setVariable("editortranslator");
193 1
                    $str .= $this->label->render($data);
194
                }
195 1
                $vars = $this->variables->toArray();
196
                $vars = array_filter($vars, function ($value) {
197 1
                    return !($value === "editor" || $value === "translator");
198 1
                });
199 1
                $this->variables->setArray($vars);
200
            }
201
        }
202
203 110
        $results = [];
204 110
        foreach ($this->variables as $var) {
205 109
            if (!empty($data->{$var})) {
206 103
                if (!empty($this->name)) {
207 102
                    $res = $this->name->render($data, $var, $citationNumber);
208 102
                    $name = $res;
209 102
                    if (!empty($this->label)) {
210 32
                        $name = $this->appendLabel($data, $var, $name);
211
                    }
212
                    //add multiple counting values
213 102
                    if (is_numeric($name) && $this->name->getForm() === "count") {
214 6
                        $results = $this->addCountValues($res, $results);
215
                    } else {
216 102
                        $results[] = $this->format($name);
217
                    }
218
                } else {
219 1
                    foreach ($data->{$var} as $name) {
220 1
                        $formatted = $this->format($name->given." ".$name->family);
221 1
                        $results[] = NameHelper::addExtendedMarkup($var, $name, $formatted);
222
                    }
223
                }
224
                // suppress substituted variables
225 103
                if (CiteProc::getContext()->getRenderingState()->getValue() === RenderingState::SUBSTITUTION) {
226 103
                    unset($data->{$var});
227
                }
228
            } else {
229 35
                if (!empty($this->substitute)) {
230 109
                    $results[] = $this->substitute->render($data);
231
                }
232
            }
233
        }
234 110
        $results = $this->filterEmpty($results);
235 110
        $str .= implode($this->delimiter, $results);
236 110
        return !empty($str) ? $this->addAffixes($str) : "";
237
    }
238
239
240
    /**
241
     * @param  $data
242
     * @param  $var
243
     * @param  $name
244
     * @return string
245
     */
246 32
    private function appendLabel($data, $var, $name)
247
    {
248 32
        $this->label->setVariable($var);
249 32
        if (in_array($this->label->getForm(), ["verb", "verb-short"])) {
250 1
            $name = $this->label->render($data).$name;
251
        } else {
252 32
            $name .= $this->label->render($data);
253
        }
254 32
        return $name;
255
    }
256
257
    /**
258
     * @param  $res
259
     * @param  $results
260
     * @return array
261
     */
262 6
    private function addCountValues($res, $results)
263
    {
264 6
        $lastElement = current($results);
265 6
        $key = key($results);
266 6
        if (!empty($lastElement)) {
267 3
            $lastElement += $res;
268 3
            $results[$key] = $lastElement;
269
        } else {
270 6
            $results[] = $res;
271
        }
272 6
        return $results;
273
    }
274
275
    /**
276
     * @return bool
277
     */
278 68
    public function hasEtAl()
279
    {
280 68
        return !empty($this->etAl);
281
    }
282
283
    /**
284
     * @return EtAl
285
     */
286 12
    public function getEtAl()
287
    {
288 12
        return $this->etAl;
289
    }
290
291
    /**
292
     * @param  EtAl $etAl
293
     * @return $this
294
     */
295 12
    public function setEtAl(EtAl $etAl)
296
    {
297 12
        $this->etAl = $etAl;
298 12
        return $this;
299
    }
300
301
    /**
302
     * @return bool
303
     */
304 53
    public function hasName()
305
    {
306 53
        return !empty($this->name);
307
    }
308
309
    /**
310
     * @return Name
311
     */
312 53
    public function getName()
313
    {
314 53
        return $this->name;
315
    }
316
317
    /**
318
     * @param  Name $name
319
     * @return $this
320
     */
321 53
    public function setName(Name $name)
322
    {
323 53
        $this->name = $name;
324 53
        return $this;
325
    }
326
327
    /**
328
     * @return string
329
     */
330 95
    public function getDelimiter()
331
    {
332 95
        return $this->delimiter;
333
    }
334
335
    /**
336
     * @return ArrayList
337
     */
338 103
    public function getVariables()
339
    {
340 103
        return $this->variables;
341
    }
342
343
    /**
344
     * @return bool
345
     */
346 53
    public function hasLabel()
347
    {
348 53
        return !empty($this->label);
349
    }
350
351
    /**
352
     * @return Label
353
     */
354 45
    public function getLabel()
355
    {
356 45
        return $this->label;
357
    }
358
359
    /**
360
     * @param Label $label
361
     */
362 45
    public function setLabel($label)
363
    {
364 45
        $this->label = $label;
365 45
    }
366
367
    /**
368
     * @return mixed
369
     */
370 62
    public function getParent()
371
    {
372 62
        return $this->parent;
373
    }
374
375 110
    private function filterEmpty(array $results)
376
    {
377
        return array_filter($results, function ($item) {
378 109
            return !empty($item);
379 110
        });
380
    }
381
382 55
    public function setParent($parent)
383
    {
384 55
        $this->parent = $parent;
385 55
    }
386
}
387