Completed
Push — master ( 63b79a...6fd4c0 )
by Sebastian
03:22
created

Names::setEtAl()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 5
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

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

This check looks for @param annotations where the type inferred by our type inference engine differs from the declared type.

It makes a suggestion as to what type it considers more descriptive.

Most often this is a case of a parameter that can be null in addition to its declared types.

Loading history...
151
     * @return string
152
     */
153
    public function render($data, $citationNumber = null)
154
    {
155
        $str = "";
156
157
        /* when the selection consists of “editor” and “translator”, and when the contents of these two name variables
158
        is identical, then the contents of only one name variable is rendered. In addition, the “editortranslator”
159
        term is used if the cs:names element contains a cs:label element, replacing the default “editor” and
160
        “translator” terms (e.g. resulting in “Doe (editor & translator)”) */
161
        if ($this->variables->hasValue("editor") && $this->variables->hasValue("translator")) {
162
            if (isset($data->editor) && isset($data->translator) && $this->sameNames($data->editor, $data->translator)) {
163
                if (isset($this->name)) {
164
                    $str .= $this->name->render($data->editor);
165
                } else {
166
                    $arr = [];
167
                    foreach ($data->editor as $editor) {
168
                        $arr[] = $this->format($editor->family . ", " . $editor->given);
169
                    }
170
                    $str .= implode($this->delimiter, $arr);
171
                }
172
                if (isset($this->label)) {
173
                    $this->label->setVariable("editortranslator");
174
                    $str .= $this->label->render($data);
175
                }
176
                $vars = $this->variables->toArray();
177
                $vars = array_filter($vars, function ($value) {
178
                    return !($value === "editor" || $value === "translator");
179
                });
180
                $this->variables->setArray($vars);
181
            }
182
        }
183
184
        $results = [];
185
186
        foreach ($this->variables as $var) {
187
188
            if (!empty($data->{$var})) {
189
                if (!empty($this->name)) {
190
                    $name = $this->name->render($data->{$var}, $citationNumber);
191
                    if (!empty($this->label)) {
192
                        $this->label->setVariable($var);
193
                        if (in_array($this->label->getForm(), ["verb", "verb-short"])) {
194
                            $name = $this->label->render($data) . $name;
195
                        } else {
196
                            $name .= $this->label->render($data);
197
                        }
198
                    }
199
                    $results[] = $this->format($name);
200
                } else {
201
                    foreach ($data->{$var} as $name) {
202
                        $results[] = $this->format($name->given . " " . $name->family);
203
                    }
204
                }
205
            } else {
206
                if (!empty($this->substitute)) {
207
                    $results[] = $this->substitute->render($data);
208
                }
209
            }
210
        }
211
        $str .= implode($this->delimiter, $results);
212
        return !empty($str) ? $this->addAffixes($str) : "";
213
    }
214
215
    /**
216
     * @return bool
217
     */
218
    public function hasEtAl()
219
    {
220
        return !empty($this->etAl);
221
    }
222
223
    /**
224
     * @return EtAl
225
     */
226
    public function getEtAl()
227
    {
228
        return $this->etAl;
229
    }
230
231
    /**
232
     * @param EtAl $etAl
233
     * @return $this
234
     */
235
    public function setEtAl(EtAl $etAl)
236
    {
237
        $this->etAl = $etAl;
238
        return $this;
239
    }
240
241
    /**
242
     * @return bool
243
     */
244
    public function hasName()
245
    {
246
        return !empty($this->name);
247
    }
248
249
    /**
250
     * @return Name
251
     */
252
    public function getName()
253
    {
254
        return $this->name;
255
    }
256
257
    /**
258
     * @param Name $name
259
     * @return $this
260
     */
261
    public function setName(Name $name)
262
    {
263
        $this->name = $name;
264
        return $this;
265
    }
266
267
    public function getDelimiter()
268
    {
269
        return $this->delimiter;
270
    }
271
272
    public function getVariables()
273
    {
274
        return $this->variables;
275
    }
276
277
    public function hasLabel()
278
    {
279
        return !empty($this->label);
280
    }
281
282
    /**
283
     * @return Label
284
     */
285
    public function getLabel()
286
    {
287
        return $this->label;
288
    }
289
290
    /**
291
     * @param Label $label
292
     */
293
    public function setLabel($label)
294
    {
295
        $this->label = $label;
296
    }
297
298
    /**
299
     * @param array $editor
300
     * @param array $translator
301
     * @return bool
302
     */
303
    private function sameNames($editor, $translator)
304
    {
305
        $same = count($editor) === count($translator);
306
307
        if (!$same) {
308
            return false;
309
        }
310
311
        array_walk($editor, function ($name, $key) use ($translator, &$same) {
312
            $family1 = $name->family;
313
            $family2 = $translator[$key]->family;
314
            $same = $same && ($family1 === $family2);
315
        });
316
317
        return (bool)$same;
318
    }
319
}