Passed
Push — master ( f36a08...6258cd )
by Sebastian
12:50
created

Name::handleSubsequentAuthorSubstitution()   B

Complexity

Conditions 7
Paths 6

Size

Total Lines 29
Code Lines 18

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 15
CRAP Score 7

Importance

Changes 0
Metric Value
cc 7
eloc 18
c 0
b 0
f 0
nc 6
nop 2
dl 0
loc 29
ccs 15
cts 15
cp 1
crap 7
rs 8.8333
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\Style\InheritableNameAttributesTrait;
17
use Seboettg\CiteProc\Style\Options\DemoteNonDroppingParticle;
18
use Seboettg\CiteProc\Style\Options\SubsequentAuthorSubstituteRule;
19
use Seboettg\CiteProc\Styles\AffixesTrait;
20
use Seboettg\CiteProc\Styles\DelimiterTrait;
21
use Seboettg\CiteProc\Styles\FormattingTrait;
22
use Seboettg\CiteProc\Util\CiteProcHelper;
23
use Seboettg\CiteProc\Util\Factory;
24
use Seboettg\CiteProc\Util\NameHelper;
25
use Seboettg\CiteProc\Util\StringHelper;
26
use SimpleXMLElement;
27
use stdClass;
28
29
/**
30
 * Class Name
31
 *
32
 * The cs:name element, an optional child element of cs:names, can be used to describe the formatting of individual
33
 * names, and the separation of names within a name variable.
34
 *
35
 * @package Seboettg\CiteProc\Rendering\Name
36
 *
37
 * @author Sebastian Böttger <[email protected]>
38
 */
39
class Name implements HasParent
40
{
41
    use InheritableNameAttributesTrait,
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\Name.
Loading history...
42
        FormattingTrait,
43
        AffixesTrait,
44
        DelimiterTrait;
45
46
    /**
47
     * @var array
48
     */
49
    protected $nameParts;
50
51
    /**
52
     * Specifies the text string used to separate names in a name variable. Default is ”, ” (e.g. “Doe, Smith”).
53
     *
54
     * @var
55
     */
56
    private $delimiter = ", ";
57
58
    /**
59
     * @var Names
60
     */
61
    private $parent;
62
63
    /**
64
     * @var SimpleXMLElement
65
     */
66
    private $node;
67
68
    /**
69
     * @var string
70
     */
71
    private $etAl;
72
73
    /**
74
     * @var string
75
     */
76
    private $variable;
77
78
    /**
79
     * Name constructor.
80
     *
81
     * @param  SimpleXMLElement $node
82
     * @param  Names            $parent
83
     * @throws InvalidStylesheetException
84
     */
85 109
    public function __construct(SimpleXMLElement $node, Names $parent)
86
    {
87 109
        $this->node = $node;
88 109
        $this->parent = $parent;
89
90 109
        $this->nameParts = [];
91
92
        /**
93
 * @var SimpleXMLElement $child 
94
*/
95 109
        foreach ($node->children() as $child) {
96 3
            switch ($child->getName()) {
97 3
            case "name-part":
98
                /**
99
 * @var NamePart $namePart 
100
*/
101 3
                $namePart = Factory::create($child, $this);
102 3
                $this->nameParts[$namePart->getName()] = $namePart;
103
            }
104
        }
105
106 109
        foreach ($node->attributes() as $attribute) {
107 93
            switch ($attribute->getName()) {
108 93
            case 'form':
109 55
                $this->form = (string)$attribute;
110 55
                break;
111
            }
112
        }
113
114 109
        $this->initFormattingAttributes($node);
115 109
        $this->initAffixesAttributes($node);
116 109
        $this->initDelimiterAttributes($node);
117 109
    }
118
119
    /**
120
     * @param  stdClass     $data
121
     * @param  string       $var
122
     * @param  integer|null $citationNumber
123
     * @return string
124
     * @throws CiteProcException
125
     */
126 97
    public function render($data, $var, $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...
127
    {
128 97
        $this->variable = $var;
129 97
        $name = $data->{$var};
130 97
        if (!$this->attributesInitialized) {
131 97
            $this->initInheritableNameAttributes($this->node);
132
        }
133 97
        if ("text" === $this->and) {
134 19
            $this->and = CiteProc::getContext()->getLocale()->filter('terms', 'and')->single;
135 88
        } elseif ('symbol' === $this->and) {
136 27
            $this->and = '&#38;';
137
        }
138
139 97
        $resultNames = $this->handleSubsequentAuthorSubstitution($name, $citationNumber);
140
141 97
        if (empty($resultNames)) {
142 2
            return CiteProc::getContext()->getCitationItems()->getSubsequentAuthorSubstitute();
143
        }
144
145 97
        $resultNames = $this->prepareAbbreviation($resultNames);
146
147
        /* When set to “true” (the default is “false”), name lists truncated by et-al abbreviation are followed by
148
        the name delimiter, the ellipsis character, and the last name of the original name list. This is only
149
        possible when the original name list has at least two more names than the truncated name list (for this
150
        the value of et-al-use-first/et-al-subsequent-min must be at least 2 less than the value of
151
        et-al-min/et-al-subsequent-use-first). */
152 97
        if ($this->etAlUseLast) {
153 3
            $this->and = "…"; // set "and"
154 3
            $this->etAl = null; //reset $etAl;
155
        }
156
157
        /* add "and" */
158 97
        $this->addAnd($resultNames);
159
160 97
        $text = $this->renderDelimiterPrecedesLast($resultNames);
161
162 97
        if (empty($text)) {
163 63
            $text = implode($this->delimiter, $resultNames);
164
        }
165
166 97
        $text = $this->appendEtAl($name, $text, $resultNames);
167
168
        /* A third value, “count”, returns the total number of names that would otherwise be rendered by the use of the
169
        cs:names element (taking into account the effects of et-al abbreviation and editor/translator collapsing),
170
        which allows for advanced sorting. */
171 97
        if ($this->form == 'count') {
172 5
            return (int)count($resultNames);
173
        }
174
175 92
        return $text;
176
    }
177
178
    /**
179
     * @param  stdClass $nameItem
180
     * @param  int      $rank
181
     * @return string
182
     * @throws CiteProcException
183
     */
184 97
    private function formatName($nameItem, $rank)
185
    {
186 97
        $nameObj = $this->cloneNamePOSC($nameItem);
187
188 97
        $useInitials = $this->initialize && !is_null($this->initializeWith) && $this->initializeWith !== false;
189 97
        if ($useInitials && isset($nameItem->given)) {
190 28
            $nameObj->given = StringHelper::initializeBySpaceOrHyphen($nameItem->given, $this->initializeWith);
191
        }
192
193 97
        $renderedResult = $this->getNamesString($nameObj, $rank);
194 97
        CiteProcHelper::applyAdditionMarkupFunction($nameItem, $this->parent->getVariables()[0], $renderedResult);
195 97
        return trim($renderedResult);
196
    }
197
198
    /**
199
     * @param  stdClass $name
200
     * @param  int      $rank
201
     * @return string
202
     * @throws CiteProcException
203
     */
204 97
    private function getNamesString($name, $rank)
205
    {
206 97
        $text = "";
207
208 97
        if (!isset($name->family)) {
209
            return $text;
210
        }
211
212 97
        $text = $this->nameOrder($name, $rank);
213
214
        //contains nbsp prefixed by normal space or followed by normal space?
215 97
        $text = htmlentities($text);
216 97
        if (strpos($text, " &nbsp;") !== false || strpos($text, "&nbsp; ") !== false) {
217
            $text = preg_replace("/[\s]+/", "", $text); //remove normal spaces
218
            return preg_replace("/&nbsp;+/", " ", $text);
219
        }
220 97
        $text = html_entity_decode(preg_replace("/[\s]+/", " ", $text));
221 97
        return $this->format(trim($text));
222
    }
223
224
    /**
225
     * @param  stdClass $name
226
     * @return stdClass
227
     */
228 97
    private function cloneNamePOSC($name)
229
    {
230 97
        $nameObj = new stdClass();
231 97
        if (isset($name->family)) {
232 97
            $nameObj->family = $name->family;
233
        }
234 97
        if (isset($name->given)) {
235 95
            $nameObj->given = $name->given;
236
        }
237 97
        if (isset($name->{'non-dropping-particle'})) {
238 8
            $nameObj->{'non-dropping-particle'} = $name->{'non-dropping-particle'};
239
        }
240 97
        if (isset($name->{'dropping-particle'})) {
241 8
            $nameObj->{'dropping-particle'} = $name->{'dropping-particle'};
242
        }
243 97
        if (isset($name->{'suffix'})) {
244 17
            $nameObj->{'suffix'} = $name->{'suffix'};
245
        }
246 97
        return $nameObj;
247
    }
248
249
    /**
250
     * @param  $data
251
     * @param  $text
252
     * @param  $resultNames
253
     * @return string
254
     */
255 97
    protected function appendEtAl($data, $text, $resultNames)
256
    {
257
        //append et al abbreviation
258 97
        if (count($data) > 1 
259 97
            && !empty($resultNames) 
260 97
            && !empty($this->etAl) 
261 97
            && !empty($this->etAlMin) 
262 97
            && !empty($this->etAlUseFirst) 
263 97
            && count($data) != count($resultNames)
264
        ) {
265
            /* By default, when a name list is truncated to a single name, the name and the “et-al” (or “and others”)
266
            term are separated by a space (e.g. “Doe et al.”). When a name list is truncated to two or more names, the
267
            name delimiter is used (e.g. “Doe, Smith, et al.”). This behavior can be changed with the
268
            delimiter-precedes-et-al attribute. */
269
270 13
            switch ($this->delimiterPrecedesEtAl) {
271 13
            case 'never':
272 5
                $text = $text . " " . $this->etAl;
273 5
                break;
274 8
            case 'always':
275 1
                $text = $text . $this->delimiter . $this->etAl;
276 1
                break;
277 8
            case 'contextual':
278
            default:
279 8
                if (count($resultNames) === 1) {
280 4
                    $text .= " " . $this->etAl;
281
                } else {
282 4
                    $text .= $this->delimiter . $this->etAl;
283
                }
284
            }
285
        }
286 97
        return $text;
287
    }
288
289
    /**
290
     * @param  $resultNames
291
     * @return array
292
     */
293 97
    protected function prepareAbbreviation($resultNames)
294
    {
295 97
        $cnt = count($resultNames);
296
        /* Use of et-al-min and et-al-user-first enables et-al abbreviation. If the number of names in a name variable
297
        matches or exceeds the number set on et-al-min, the rendered name list is truncated after reaching the number of
298
        names set on et-al-use-first.  */
299
300 97
        if (isset($this->etAlMin) && isset($this->etAlUseFirst)) {
301 24
            if ($this->etAlMin <= $cnt) {
302 16
                if ($this->etAlUseLast && $this->etAlMin - $this->etAlUseFirst >= 2) {
303
                    /* et-al-use-last: When set to “true” (the default is “false”), name lists truncated by et-al
304
                    abbreviation are followed by the name delimiter, the ellipsis character, and the last name of the
305
                    original name list. This is only possible when the original name list has at least two more names
306
                    than the truncated name list (for this the value of et-al-use-first/et-al-subsequent-min must be at
307
                    least 2 less than the value of et-al-min/et-al-subsequent-use-first).*/
308
309 3
                    $lastName = array_pop($resultNames); //remove last Element and remember in $lastName
310
                }
311 16
                for ($i = $this->etAlUseFirst; $i < $cnt; ++$i) {
312 16
                    unset($resultNames[$i]);
313
                }
314
315 16
                $resultNames = array_values($resultNames);
316
317 16
                if (!empty($lastName)) { // append $lastName if exist
318 3
                    $resultNames[] = $lastName;
319
                }
320
321 16
                if ($this->parent->hasEtAl()) {
322
                    $this->etAl = $this->parent->getEtAl()->render(null);
323
                    return $resultNames;
324
                } else {
325 16
                    $this->etAl = CiteProc::getContext()->getLocale()->filter('terms', 'et-al')->single;
326 16
                    return $resultNames;
327
                }
328
            }
329 16
            return $resultNames;
330
        }
331 76
        return $resultNames;
332
    }
333
334
    /**
335
     * @param  $data
336
     * @param  stdClass $preceding
337
     * @return array
338
     * @throws CiteProcException
339
     */
340 3
    protected function renderSubsequentSubstitution($data, $preceding)
341
    {
342 3
        $resultNames = [];
343 3
        $subsequentSubstitution = CiteProc::getContext()->getCitationItems()->getSubsequentAuthorSubstitute();
344 3
        $subsequentSubstitutionRule = CiteProc::getContext()->getCitationItems()->getSubsequentAuthorSubstituteRule();
345
346
        /**
347
         * @var string $type
348
         * @var stdClass $name
349
         */
350 3
        foreach ($data as $rank => $name) {
351
            switch ($subsequentSubstitutionRule) {
352
                /* “partial-each” - when one or more rendered names in the name variable match those in the preceding
353
                bibliographic entry, the value of subsequent-author-substitute substitutes for each matching name.
354
                Matching starts with the first name, and continues up to the first mismatch. */
355 3
            case SubsequentAuthorSubstituteRule::PARTIAL_EACH:
356 1
                if (NameHelper::precedingHasAuthor($preceding, $name)) {
357 1
                    $resultNames[] = $subsequentSubstitution;
358
                } else {
359 1
                    $resultNames[] = $this->formatName($name, $rank);
360
                }
361 1
                break;
362
                /* “partial-first” - as “partial-each”, but substitution is limited to the first name of the name
363
                variable. */
364 2
            case SubsequentAuthorSubstituteRule::PARTIAL_FIRST:
365 1
                if ($rank === 0) {
366 1
                    if ($preceding->author[0]->family === $name->family) {
367 1
                        $resultNames[] = $subsequentSubstitution;
368
                    } else {
369 1
                        $resultNames[] = $this->formatName($name, $rank);
370
                    }
371
                } else {
372 1
                    $resultNames[] = $this->formatName($name, $rank);
373
                }
374 1
                break;
375
376
                /* “complete-each” - requires a complete match like “complete-all”, but now the value of
377
                subsequent-author-substitute substitutes for each rendered name. */
378 1
            case SubsequentAuthorSubstituteRule::COMPLETE_EACH:
379
                try {
380 1
                    if (NameHelper::identicalAuthors($preceding, $data)) {
381 1
                        $resultNames[] = $subsequentSubstitution;
382
                    } else {
383 1
                        $resultNames[] = $this->formatName($name, $rank);
384
                    }
385
                } catch (CiteProcException $e) {
386
                    $resultNames[] = $this->formatName($name, $rank);
387
                }
388 1
                break;
389
            }
390
        }
391 3
        return $resultNames;
392
    }
393
394
    /**
395
     * @param  array $data
396
     * @param  int   $citationNumber
397
     * @return array
398
     * @throws CiteProcException
399
     */
400 97
    private function handleSubsequentAuthorSubstitution($data, $citationNumber)
401
    {
402 97
        $hasPreceding = CiteProc::getContext()->getCitationItems()->hasKey($citationNumber - 1);
403 97
        $subsequentSubstitution = CiteProc::getContext()->getCitationItems()->getSubsequentAuthorSubstitute();
404 97
        $subsequentSubstitutionRule = CiteProc::getContext()->getCitationItems()->getSubsequentAuthorSubstituteRule();
405 97
        $preceding = CiteProc::getContext()->getCitationItems()->get($citationNumber - 1);
406
407
408 97
        if ($hasPreceding && !is_null($subsequentSubstitution) && !empty($subsequentSubstitutionRule)) {
409
            /**
410
             * @var stdClass $preceding
411
             */
412 8
            if ($subsequentSubstitutionRule == SubsequentAuthorSubstituteRule::COMPLETE_ALL) {
0 ignored issues
show
introduced by
The condition $subsequentSubstitutionR...ituteRule::COMPLETE_ALL is always false.
Loading history...
413
                try {
414 5
                    if (NameHelper::identicalAuthors($preceding, $data)) {
415 2
                        return [];
416
                    } else {
417 4
                        $resultNames = $this->getFormattedNames($data);
418
                    }
419 1
                } catch (CiteProcException $e) {
420 5
                    $resultNames = $this->getFormattedNames($data);
421
                }
422
            } else {
423 8
                $resultNames = $this->renderSubsequentSubstitution($data, $preceding);
424
            }
425
        } else {
426 97
            $resultNames = $this->getFormattedNames($data);
427
        }
428 97
        return $resultNames;
429
    }
430
431
432
    /**
433
     * @param  array $data
434
     * @return array
435
     * @throws CiteProcException
436
     */
437 97
    protected function getFormattedNames($data)
438
    {
439 97
        $resultNames = [];
440 97
        foreach ($data as $rank => $name) {
441 97
            $formatted = $this->formatName($name, $rank);
442 97
            $resultNames[] = NameHelper::addExtendedMarkup($this->variable, $name, $formatted);
443
        }
444 97
        return $resultNames;
445
    }
446
447
    /**
448
     * @param  $resultNames
449
     * @return string
450
     */
451 12
    protected function renderDelimiterPrecedesLastNever($resultNames)
452
    {
453 12
        $text = "";
454 12
        if (!$this->etAlUseLast) {
455 10
            if (count($resultNames) === 1) {
456 7
                $text = $resultNames[0];
457 9
            } elseif (count($resultNames) === 2) {
458 8
                $text = implode(" ", $resultNames);
459
            } else { // >2
460 6
                $lastName = array_pop($resultNames);
461 6
                $text = implode($this->delimiter, $resultNames) . " " . $lastName;
462
            }
463
        }
464 12
        return $text;
465
    }
466
467
    /**
468
     * @param  $resultNames
469
     * @return string
470
     */
471 14
    protected function renderDelimiterPrecedesLastContextual($resultNames)
472
    {
473 14
        if (count($resultNames) === 1) {
474 9
            $text = $resultNames[0];
475 6
        } elseif (count($resultNames) === 2) {
476 5
            $text = implode(" ", $resultNames);
477
        } else {
478 1
            $text = implode($this->delimiter, $resultNames);
479
        }
480 14
        return $text;
481
    }
482
483
    /**
484
     * @param $resultNames
485
     */
486 97
    protected function addAnd(&$resultNames)
487
    {
488 97
        $count = count($resultNames);
489 97
        if (!empty($this->and) && $count > 1 && empty($this->etAl)) {
490 27
            $new = $this->and . ' ' . end($resultNames); // add and-prefix of the last name if "and" is defined
491
            // set prefixed last name at the last position of $resultNames array
492 27
            $resultNames[count($resultNames) - 1] = $new;
493
        }
494 97
    }
495
496
    /**
497
     * @param  $resultNames
498
     * @return array|string
499
     */
500 97
    protected function renderDelimiterPrecedesLast($resultNames)
501
    {
502 97
        $text = "";
503 97
        if (!empty($this->and) && empty($this->etAl)) {
504 45
            switch ($this->delimiterPrecedesLast) {
505 45
            case 'after-inverted-name':
506
                //TODO: implement
507
                break;
508 45
            case 'always':
509 22
                $text = implode($this->delimiter, $resultNames);
510 22
                break;
511 26
            case 'never':
512 12
                $text = $this->renderDelimiterPrecedesLastNever($resultNames);
513 12
                break;
514 14
            case 'contextual':
515
            default:
516 14
                $text = $this->renderDelimiterPrecedesLastContextual($resultNames);
517
            }
518
        }
519 97
        return $text;
520
    }
521
522
523
    /**
524
     * @param stdClass $data
525
     * @param integer  $rank
526
     *
527
     * @return string
528
     * @throws CiteProcException
529
     */
530 97
    private function nameOrder($data, $rank)
531
    {
532 97
        $nameAsSortOrder = (($this->nameAsSortOrder === "first" && $rank === 0) || $this->nameAsSortOrder === "all");
533 97
        $demoteNonDroppingParticle = CiteProc::getContext()->getGlobalOptions()->getDemoteNonDroppingParticles();
534 97
        $normalizedName = NameHelper::normalizeName($data);
535 97
        if (StringHelper::isLatinString($normalizedName) || StringHelper::isCyrillicString($normalizedName)) {
536 96
            if ($this->form === "long"
537 96
                && $nameAsSortOrder
538 40
                && ((string)$demoteNonDroppingParticle === DemoteNonDroppingParticle::NEVER
539 96
                || (string)$demoteNonDroppingParticle === DemoteNonDroppingParticle::SORT_ONLY)
540
            ) {
541
                // [La] [Fontaine], [Jean] [de], [III]
542 25
                NameHelper::prependParticleTo($data, "family", "non-dropping-particle");
543 25
                NameHelper::appendParticleTo($data, "given", "dropping-particle");
544
545 25
                list($family, $given) = $this->renderNameParts($data);
546
547 25
                $text = $family . (!empty($given) ? $this->sortSeparator . $given : "");
548 25
                $text .= !empty($data->suffix) ? $this->sortSeparator . $data->suffix : "";
549 76
            } elseif ($this->form === "long"
550 76
                && $nameAsSortOrder
551 15
                && (is_null($demoteNonDroppingParticle)
552 76
                || (string)$demoteNonDroppingParticle === DemoteNonDroppingParticle::DISPLAY_AND_SORT)
553
            ) {
554
                // [Fontaine], [Jean] [de] [La], [III]
555 15
                NameHelper::appendParticleTo($data, "given", "dropping-particle");
556 15
                NameHelper::appendParticleTo($data, "given", "non-dropping-particle");
557 15
                list($family, $given) = $this->renderNameParts($data);
558 15
                $text = $family;
559 15
                $text .= !empty($given) ? $this->sortSeparator . $given : "";
560 15
                $text .= !empty($data->suffix) ? $this->sortSeparator . $data->suffix : "";
561 68
            } elseif ($this->form === "long" && $nameAsSortOrder && empty($demoteNonDroppingParticle)) {
562
                list($family, $given) = $this->renderNameParts($data);
563
                $text = $family;
564
                $text .= !empty($given) ? $this->delimiter . $given : "";
565
                $text .= !empty($data->suffix) ? $this->sortSeparator . $data->suffix : "";
566 68
            } elseif ($this->form === "short") {
567
                // [La] [Fontaine]
568 22
                NameHelper::prependParticleTo($data, "family", "non-dropping-particle");
569 22
                $text = $data->family;
570
            } else {// form "long" (default)
571
                // [Jean] [de] [La] [Fontaine] [III]
572 47
                NameHelper::prependParticleTo($data, "family", "non-dropping-particle");
573 47
                NameHelper::prependParticleTo($data, "family", "dropping-particle");
574 47
                NameHelper::appendParticleTo($data, "family", "suffix");
575 47
                list($family, $given) = $this->renderNameParts($data);
576 96
                $text = !empty($given) ? $given . " " . $family : $family;
577
            }
578
        } else {
579 1
            $text = $this->form === "long" ? $data->family.$data->given : $data->family;
580
        }
581
582 97
        return $text;
583
    }
584
585
    /**
586
     * @param  $data
587
     * @return array
588
     */
589 79
    private function renderNameParts($data)
590
    {
591 79
        $given = "";
592 79
        if (array_key_exists("family", $this->nameParts)) {
593 2
            $family = $this->nameParts["family"]->render($data);
594
        } else {
595 77
            $family = $data->family;
596
        }
597 79
        if (isset($data->given)) {
598 77
            if (array_key_exists("given", $this->nameParts)) {
599 2
                $given = $this->nameParts["given"]->render($data);
600
            } else {
601 75
                $given = $data->given;
602
            }
603
        }
604 79
        return [$family, $given];
605
    }
606
607
608
    /**
609
     * @return string
610
     */
611 5
    public function getForm()
612
    {
613 5
        return $this->form;
614
    }
615
616
    /**
617
     * @return string
618
     */
619
    public function isNameAsSortOrder()
620
    {
621
        return $this->nameAsSortOrder;
622
    }
623
624
    /**
625
     * @return string
626
     */
627
    public function getDelimiter()
628
    {
629
        return $this->delimiter;
630
    }
631
632
    /**
633
     * @param mixed $delimiter
634
     */
635 97
    public function setDelimiter($delimiter)
636
    {
637 97
        $this->delimiter = $delimiter;
638 97
    }
639
640
    /**
641
     * @return Names
642
     */
643
    public function getParent()
644
    {
645
        return $this->parent;
646
    }
647
}
648