20 use Seboettg\CiteProc\Styles\DelimiterTrait;
41 use InheritableNameAttributesTrait,
56 private $delimiter =
", ";
88 $this->parent = $parent;
90 $this->nameParts = [];
95 foreach ($node->children() as $child) {
96 switch ($child->getName()) {
100 $this->nameParts[$namePart->getName()] = $namePart;
104 foreach ($node->attributes() as $attribute) {
105 switch ($attribute->getName()) {
107 $this->form = (string) $attribute;
112 $this->initFormattingAttributes($node);
113 $this->initAffixesAttributes($node);
114 $this->initDelimiterAttributes($node);
124 public function render($data, $var, $citationNumber =
null)
126 $this->variable = $var;
127 $name = $data->{$var};
128 if (!$this->attributesInitialized) {
131 if (
"text" === $this->and) {
133 } elseif (
'symbol' === $this->and) {
134 $this->and =
'&';
137 $resultNames = $this->handleSubsequentAuthorSubstitution($name, $citationNumber);
139 if (empty($resultNames)) {
150 if ($this->etAlUseLast) {
156 $this->
addAnd($resultNames);
161 $text = implode($this->delimiter, $resultNames);
164 $text = $this->
appendEtAl($name, $text, $resultNames);
169 if ($this->form ==
'count') {
170 return (
int) count($resultNames);
182 private function formatName($nameItem, $rank)
184 $nameObj = $this->cloneNamePOSC($nameItem);
186 $useInitials = $this->initialize && !is_null($this->initializeWith) && $this->initializeWith !==
false;
187 if ($useInitials && isset($nameItem->given)) {
191 $renderedResult = $this->getNamesString($nameObj, $rank);
193 return trim($renderedResult);
202 private function getNamesString($name, $rank)
206 if (!isset($name->family)) {
210 $text = $this->nameOrder($name, $rank);
213 $text = htmlentities($text);
214 if (strpos($text,
" ") !==
false || strpos($text,
" ") !==
false) {
215 $text = preg_replace(
"/[\s]+/",
"", $text);
216 return preg_replace(
"/ +/",
" ", $text);
218 $text = html_entity_decode(preg_replace(
"/[\s]+/",
" ", $text));
219 return $this->
format(trim($text));
226 private function cloneNamePOSC($name)
228 $nameObj =
new stdClass();
229 if (isset($name->family)) {
230 $nameObj->family = $name->family;
232 if (isset($name->given)) {
233 $nameObj->given = $name->given;
235 if (isset($name->{
'non-dropping-particle'})) {
236 $nameObj->{
'non-dropping-particle'} = $name->{
'non-dropping-particle'};
238 if (isset($name->{
'dropping-particle'})) {
239 $nameObj->{
'dropping-particle'} = $name->{
'dropping-particle'};
241 if (isset($name->{
'suffix'})) {
242 $nameObj->{
'suffix'} = $name->{
'suffix'};
253 protected function appendEtAl($data, $text, $resultNames)
257 && !empty($resultNames)
258 && !empty($this->etAl)
259 && !empty($this->etAlMin)
260 && !empty($this->etAlUseFirst)
268 switch ($this->delimiterPrecedesEtAl) {
270 $text = $text .
" " . $this->etAl;
273 $text = $text . $this->delimiter . $this->etAl;
277 if (count($resultNames) === 1) {
278 $text .=
" " . $this->etAl;
280 $text .= $this->delimiter . $this->etAl;
291 protected function prepareAbbreviation($resultNames)
293 $cnt = count($resultNames);
298 if (isset($this->etAlMin) && isset($this->etAlUseFirst)) {
299 if ($this->etAlMin <= $cnt) {
300 if ($this->etAlUseLast && $this->etAlMin - $this->etAlUseFirst >= 2) {
307 $lastName = array_pop($resultNames);
309 for ($i = $this->etAlUseFirst; $i < $cnt; ++$i) {
310 unset($resultNames[$i]);
313 $resultNames = array_values($resultNames);
315 if (!empty($lastName)) {
316 $resultNames[] = $lastName;
319 if ($this->parent->hasEtAl()) {
320 $this->etAl = $this->parent->getEtAl()->render(
null);
323 $this->etAl = CiteProc::getContext()->getLocale()->filter(
'terms',
'et-al')->single;
338 protected function renderSubsequentSubstitution($data, $preceding)
341 $subsequentSubstitution = CiteProc::getContext()->getCitationItems()->getSubsequentAuthorSubstitute();
342 $subsequentSubstitutionRule = CiteProc::getContext()->getCitationItems()->getSubsequentAuthorSubstituteRule();
348 foreach ($data as $rank => $name) {
349 switch ($subsequentSubstitutionRule) {
353 case SubsequentAuthorSubstituteRule::PARTIAL_EACH:
354 if (NameHelper::precedingHasAuthor($preceding, $name)) {
355 $resultNames[] = $subsequentSubstitution;
357 $resultNames[] = $this->formatName($name, $rank);
362 case SubsequentAuthorSubstituteRule::PARTIAL_FIRST:
364 if ($preceding->author[0]->family === $name->family) {
365 $resultNames[] = $subsequentSubstitution;
367 $resultNames[] = $this->formatName($name, $rank);
370 $resultNames[] = $this->formatName($name, $rank);
376 case SubsequentAuthorSubstituteRule::COMPLETE_EACH:
378 if (NameHelper::identicalAuthors($preceding, $data)) {
379 $resultNames[] = $subsequentSubstitution;
381 $resultNames[] = $this->formatName($name, $rank);
383 }
catch (CiteProcException $e) {
384 $resultNames[] = $this->formatName($name, $rank);
398 private function handleSubsequentAuthorSubstitution($data, $citationNumber)
400 $hasPreceding = CiteProc::getContext()->getCitationItems()->hasKey($citationNumber - 1);
401 $subsequentSubstitution = CiteProc::getContext()->getCitationItems()->getSubsequentAuthorSubstitute();
402 $subsequentSubstitutionRule = CiteProc::getContext()->getCitationItems()->getSubsequentAuthorSubstituteRule();
403 $preceding = CiteProc::getContext()->getCitationItems()->get($citationNumber - 1);
406 if ($hasPreceding && !is_null($subsequentSubstitution) && !empty($subsequentSubstitutionRule)) {
410 if ($subsequentSubstitutionRule == SubsequentAuthorSubstituteRule::COMPLETE_ALL) {
412 if (NameHelper::identicalAuthors($preceding, $data)) {
415 $resultNames = $this->getFormattedNames($data);
417 }
catch (CiteProcException $e) {
418 $resultNames = $this->getFormattedNames($data);
421 $resultNames = $this->renderSubsequentSubstitution($data, $preceding);
424 $resultNames = $this->getFormattedNames($data);
435 protected function getFormattedNames($data)
438 foreach ($data as $rank => $name) {
439 $formatted = $this->formatName($name, $rank);
440 $resultNames[] = NameHelper::addExtendedMarkup($this->variable, $name, $formatted);
449 protected function renderDelimiterPrecedesLastNever($resultNames)
452 if (!$this->etAlUseLast) {
453 if (count($resultNames) === 1) {
454 $text = $resultNames[0];
455 } elseif (count($resultNames) === 2) {
456 $text = implode(
" ", $resultNames);
458 $lastName = array_pop($resultNames);
459 $text = implode($this->delimiter, $resultNames).
" ".$lastName;
469 protected function renderDelimiterPrecedesLastContextual($resultNames)
471 if (count($resultNames) === 1) {
472 $text = $resultNames[0];
473 } elseif (count($resultNames) === 2) {
474 $text = implode(
" ", $resultNames);
476 $text = implode($this->delimiter, $resultNames);
484 protected function addAnd(&$resultNames)
486 $count = count($resultNames);
487 if (!empty($this->and) && $count > 1 && empty($this->etAl)) {
488 $new = $this->and.
' '.end($resultNames);
490 $resultNames[count($resultNames) - 1] = $new;
498 protected function renderDelimiterPrecedesLast($resultNames)
501 if (!empty($this->and) && empty($this->etAl)) {
502 switch ($this->delimiterPrecedesLast) {
503 case 'after-inverted-name':
507 $text = implode($this->delimiter, $resultNames);
510 $text = $this->renderDelimiterPrecedesLastNever($resultNames);
514 $text = $this->renderDelimiterPrecedesLastContextual($resultNames);
528 private function nameOrder($data, $rank)
530 $nameAsSortOrder = (($this->nameAsSortOrder ===
"first" && $rank === 0) || $this->nameAsSortOrder ===
"all");
531 $demoteNonDroppingParticle = CiteProc::getContext()->getGlobalOptions()->getDemoteNonDroppingParticles();
532 $normalizedName = NameHelper::normalizeName($data);
533 if (StringHelper::isLatinString($normalizedName) || StringHelper::isCyrillicString($normalizedName)) {
534 if ($this->form ===
"long"
536 && ((
string) $demoteNonDroppingParticle === DemoteNonDroppingParticle::NEVER
537 || (
string) $demoteNonDroppingParticle === DemoteNonDroppingParticle::SORT_ONLY)
540 NameHelper::prependParticleTo($data,
"family",
"non-dropping-particle");
541 NameHelper::appendParticleTo($data,
"given",
"dropping-particle");
543 list($family, $given) = $this->renderNameParts($data);
545 $text = $family.(!empty($given) ? $this->sortSeparator.$given :
"");
546 $text .= !empty($data->suffix) ? $this->sortSeparator.$data->suffix :
"";
547 } elseif ($this->form ===
"long"
549 && (is_null($demoteNonDroppingParticle)
550 || (
string) $demoteNonDroppingParticle === DemoteNonDroppingParticle::DISPLAY_AND_SORT)
553 NameHelper::appendParticleTo($data,
"given",
"dropping-particle");
554 NameHelper::appendParticleTo($data,
"given",
"non-dropping-particle");
555 list($family, $given) = $this->renderNameParts($data);
557 $text .= !empty($given) ? $this->sortSeparator.$given :
"";
558 $text .= !empty($data->suffix) ? $this->sortSeparator.$data->suffix :
"";
559 } elseif ($this->form ===
"long" && $nameAsSortOrder && empty($demoteNonDroppingParticle)) {
560 list($family, $given) = $this->renderNameParts($data);
562 $text .= !empty($given) ? $this->delimiter.$given :
"";
563 $text .= !empty($data->suffix) ? $this->sortSeparator.$data->suffix :
"";
564 } elseif ($this->form ===
"short") {
566 NameHelper::prependParticleTo($data,
"family",
"non-dropping-particle");
567 $text = $data->family;
570 NameHelper::prependParticleTo($data,
"family",
"non-dropping-particle");
571 NameHelper::prependParticleTo($data,
"family",
"dropping-particle");
572 NameHelper::appendParticleTo($data,
"family",
"suffix");
573 list($family, $given) = $this->renderNameParts($data);
574 $text = !empty($given) ? $given.
" ".$family : $family;
576 } elseif (StringHelper::isAsianString(NameHelper::normalizeName($data))) {
577 $text = $this->form ===
"long" ? $data->family . $data->given : $data->family;
579 $text = $this->form ===
"long" ? $data->family .
" " . $data->given : $data->family;
588 private function renderNameParts($data)
591 if (array_key_exists(
"family", $this->nameParts)) {
592 $family = $this->nameParts[
"family"]->render($data);
594 $family = $data->family;
596 if (isset($data->given)) {
597 if (array_key_exists(
"given", $this->nameParts)) {
598 $given = $this->nameParts[
"given"]->render($data);
600 $given = $data->given;
603 return [$family, $given];
618 public function isNameAsSortOrder()
620 return $this->nameAsSortOrder;
626 public function getDelimiter()
628 return $this->delimiter;
634 public function setDelimiter($delimiter)
636 $this->delimiter = $delimiter;
642 public function getParent()
644 return $this->parent;