Completed
Push — master ( 5d3cd3...160b9c )
by Sebastian
10:22
created

Names::sameNames()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 16
Code Lines 9

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 16
rs 9.4285
cc 3
eloc 9
nc 2
nop 2

2 Methods

Rating   Name   Duplication   Size   Complexity  
A Names::getVariables() 0 4 1
A Names::hasLabel() 0 4 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\Rendering\HasParent;
13
use Seboettg\CiteProc\Rendering\Label;
14
use Seboettg\CiteProc\Rendering\Rendering;
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\CiteProc\Util\NameHelper;
20
use Seboettg\Collection\ArrayList;
21
22
23
/**
24
 * Class Group
25
 * @package Seboettg\CiteProc\Rendering
26
 *
27
 * @author Sebastian Böttger <[email protected]>
28
 */
29
class Names implements Rendering, HasParent
30
{
31
    use DelimiterTrait;
32
    use AffixesTrait;
33
    use FormattingTrait;
34
35
    /**
36
     * Variables (selected with the required variable attribute), each of which can contain multiple names (e.g. the
37
     * “author” variable contains all the author names of the cited item). If multiple variables are selected
38
     * (separated by single spaces, see example below), each variable is independently rendered in the order specified.
39
     *
40
     * @var ArrayList
41
     */
42
    private $variables;
43
44
    /**
45
     * The Name element, an optional child element of Names, can be used to describe the formatting of individual
46
     * names, and the separation of names within a name variable.
47
     *
48
     * @var Name
49
     */
50
    private $name;
51
52
    /**
53
     * The optional Label element must be included after the Name and EtAl elements, but before
54
     * the Substitute element. When used as a child element of Names, Label does not carry the variable
55
     * attribute; it uses the variable(s) set on the parent Names element instead.
56
     *
57
     * @var Label
58
     */
59
    private $label;
60
61
    /**
62
     * The optional Substitute element, which must be included as the last child element of Names, adds
63
     * substitution in case the name variables specified in the parent cs:names element are empty. The substitutions
64
     * are specified as child elements of Substitute, and must consist of one or more rendering elements (with the
65
     * exception of Layout). A shorthand version of Names without child elements, which inherits the attributes
66
     * values set on the cs:name and EtAl child elements of the original Names element, may also be used. If
67
     * Substitute contains multiple child elements, the first element to return a non-empty result is used for
68
     * substitution. Substituted variables are suppressed in the rest of the output to prevent duplication. An example,
69
     * where an empty “author” name variable is substituted by the “editor” name variable, or, when no editors exist,
70
     * by the “title” macro:
71
     *
72
     * <macro name="author">
73
     *     <names variable="author">
74
     *         <substitute>
75
     *             <names variable="editor"/>
76
     *             <text macro="title"/>
77
     *         </substitute>
78
     *     </names>
79
     * </macro>
80
     *
81
     * @var Substitute
82
     */
83
    private $substitute;
84
85
    /**
86
     * Et-al abbreviation, controlled via the et-al-... attributes (see Name), can be further customized with the
87
     * optional cs:et-al element, which must follow the cs:name element (if present). The term attribute may be set to
88
     * either “et-al” (the default) or to “and others” to use either term. The formatting attributes may also be used,
89
     * for example to italicize the “et-al” term:
90
     *
91
     * @var EtAl
92
     */
93
    private $etAl;
94
95
    /**
96
     * The delimiter attribute may be set on cs:names to separate the names of the different name variables (e.g. the
97
     * semicolon in “Doe, Smith (editors); Johnson (translator)”).
98
     *
99
     * @var string
100
     */
101
    private $delimiter = ", ";
102
103
    private $parent;
104
105
    /**
106
     * Names constructor.
107
     * @param \SimpleXMLElement $node
108
     */
109
    public function __construct(\SimpleXMLElement $node, $parent)
110
    {
111
        $this->parent = $parent;
112
        /** @var \SimpleXMLElement $child */
113
        foreach ($node->children() as $child) {
114
115
            switch ($child->getName()) {
116
                case "name":
117
                    $this->name = Factory::create($child, $this);
118
                    break;
119
                case "label":
120
                    $this->label = Factory::create($child);
121
                    break;
122
                case "substitute":
123
                    $this->substitute = new Substitute($child, $this);
124
                    break;
125
                case "et-al":
126
                    $this->etAl = Factory::create($child);
127
            }
128
        }
129
130
        /** @var \SimpleXMLElement $attribute */
131
        foreach ($node->attributes() as $attribute) {
132
            if ("variable" === $attribute->getName()) {
133
                $this->variables = new ArrayList(explode(" ", (string) $attribute));
134
                break;
135
            }
136
        }
137
138
        $this->initDelimiterAttributes($node);
139
        $this->initAffixesAttributes($node);
140
        $this->initFormattingAttributes($node);
141
    }
142
143
    /**
144
     * This outputs the contents of one or more name variables (selected with the required variable attribute), each
145
     * of which can contain multiple names (e.g. the “author” variable contains all the author names of the cited item).
146
     * If multiple variables are selected (separated by single spaces), each variable is independently rendered in the
147
     * order specified, with one exception: when the selection consists of “editor” and “translator”, and when the
148
     * contents of these two name variables is identical, then the contents of only one name variable is rendered. In
149
     * addition, the “editortranslator” term is used if the Names element contains a Label element, replacing the
150
     * default “editor” and “translator” terms (e.g. resulting in “Doe (editor & translator)”).
151
     *
152
     * @param \stdClass $data
153
     * @param int|null $citationNumber
154
     * @return string
155
     */
156
    public function render($data, $citationNumber = null)
157
    {
158
        $str = "";
159
160
        /* when the selection consists of “editor” and “translator”, and when the contents of these two name variables
161
        is identical, then the contents of only one name variable is rendered. In addition, the “editortranslator”
162
        term is used if the cs:names element contains a cs:label element, replacing the default “editor” and
163
        “translator” terms (e.g. resulting in “Doe (editor & translator)”) */
164
        if ($this->variables->hasValue("editor") && $this->variables->hasValue("translator")) {
165
            if (isset($data->editor) && isset($data->translator) && NameHelper::sameNames($data->editor, $data->translator)) {
166
                if (isset($this->name)) {
167
                    $str .= $this->name->render($data->editor);
168
                } else {
169
                    $arr = [];
170
                    foreach ($data->editor as $editor) {
171
                        $arr[] = $this->format($editor->family . ", " . $editor->given);
172
                    }
173
                    $str .= implode($this->delimiter, $arr);
174
                }
175
                if (isset($this->label)) {
176
                    $this->label->setVariable("editortranslator");
177
                    $str .= $this->label->render($data);
178
                }
179
                $vars = $this->variables->toArray();
180
                $vars = array_filter($vars, function($value) {
181
                    return !($value === "editor" || $value === "translator");
182
                });
183
                $this->variables->setArray($vars);
184
            }
185
        }
186
187
        $results = [];
188
        foreach ($this->variables as $var) {
189
190
            if (!empty($data->{$var})) {
191
                if (!empty($this->name)) {
192
                    $res = $this->name->render($data->{$var}, $citationNumber);
193
                    $name = $res;
194
                    if (!empty($this->label)) {
195
                        $name = $this->appendLabel($data, $var, $name);
196
                    }
197
                    //add multiple counting values
198
                    if (is_numeric($name) && $this->name->getForm() === "count") {
199
                        $results = $this->addCountValues($res, $results);
200
                    } else {
201
                        $results[] = $this->format($name);
202
                    }
203
                } else {
204
                    foreach ($data->{$var} as $name) {
205
                        $results[] = $this->format($name->given . " " . $name->family);
206
                    }
207
                }
208
            } else {
209
                if (!empty($this->substitute)) {
210
                    $results[] = $this->substitute->render($data);
211
                }
212
            }
213
        }
214
        $str .= implode($this->delimiter, $results);
215
        return !empty($str) ? $this->addAffixes($str) : "";
216
    }
217
218
219
    /**
220
     * @param $data
221
     * @param $var
222
     * @param $name
223
     * @return string
224
     */
225
    private function appendLabel($data, $var, $name)
226
    {
227
        $this->label->setVariable($var);
228
        if (in_array($this->label->getForm(), ["verb", "verb-short"])) {
229
            $name = $this->label->render($data) . $name;
230
        } else {
231
            $name .= $this->label->render($data);
232
        }
233
        return $name;
234
    }
235
236
    /**
237
     * @param $res
238
     * @param $results
239
     * @return array
240
     */
241
    private function addCountValues($res, $results)
242
    {
243
        $lastElement = current($results);
244
        $key = key($results);
245
        if (!empty($lastElement)) {
246
            $lastElement += $res;
247
            $results[$key] = $lastElement;
248
        } else {
249
            $results[] = $res;
250
        }
251
        return $results;
252
    }
253
254
    /**
255
     * @return bool
256
     */
257
    public function hasEtAl()
258
    {
259
        return !empty($this->etAl);
260
    }
261
262
    /**
263
     * @return EtAl
264
     */
265
    public function getEtAl()
266
    {
267
        return $this->etAl;
268
    }
269
270
    /**
271
     * @param EtAl $etAl
272
     * @return $this
273
     */
274
    public function setEtAl(EtAl $etAl)
275
    {
276
        $this->etAl = $etAl;
277
        return $this;
278
    }
279
280
    /**
281
     * @return bool
282
     */
283
    public function hasName()
284
    {
285
        return !empty($this->name);
286
    }
287
288
    /**
289
     * @return Name
290
     */
291
    public function getName()
292
    {
293
        return $this->name;
294
    }
295
296
    /**
297
     * @param Name $name
298
     * @return $this
299
     */
300
    public function setName(Name $name)
301
    {
302
        $this->name = $name;
303
        return $this;
304
    }
305
306
    /**
307
     * @return string
308
     */
309
    public function getDelimiter()
310
    {
311
        return $this->delimiter;
312
    }
313
314
    /**
315
     * @return ArrayList
316
     */
317
    public function getVariables()
318
    {
319
        return $this->variables;
320
    }
321
322
    /**
323
     * @return bool
324
     */
325
    public function hasLabel()
326
    {
327
        return !empty($this->label);
328
    }
329
330
    /**
331
     * @return Label
332
     */
333
    public function getLabel()
334
    {
335
        return $this->label;
336
    }
337
338
    /**
339
     * @param Label $label
340
     */
341
    public function setLabel($label)
342
    {
343
        $this->label = $label;
344
    }
345
346
    /**
347
     * @return mixed
348
     */
349
    public function getParent()
350
    {
351
        return $this->parent;
352
    }
353
354
}