Layout::wrapBibEntry()   A
last analyzed

Complexity

Conditions 1
Paths 1

Size

Total Lines 7
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 1
eloc 5
c 1
b 0
f 0
nc 1
nop 2
dl 0
loc 7
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;
11
12
use Seboettg\CiteProc\CiteProc;
13
use Seboettg\CiteProc\Data\DataList;
14
use Seboettg\CiteProc\Exception\InvalidStylesheetException;
15
use Seboettg\CiteProc\RenderingState;
16
use Seboettg\CiteProc\Style\StyleElement;
17
use Seboettg\CiteProc\Styles\AffixesTrait;
18
use Seboettg\CiteProc\Styles\ConsecutivePunctuationCharacterTrait;
19
use Seboettg\CiteProc\Styles\DelimiterTrait;
20
use Seboettg\CiteProc\Styles\FormattingTrait;
21
use Seboettg\CiteProc\Util\CiteProcHelper;
22
use Seboettg\CiteProc\Util\Factory;
23
use Seboettg\CiteProc\Util\StringHelper;
24
use Seboettg\Collection\ArrayList;
25
use Seboettg\Collection\Lists\ListInterface;
26
use Seboettg\Collection\Map\MapInterface;
27
use SimpleXMLElement;
28
use stdClass;
29
use function Seboettg\Collection\Lists\emptyList;
30
31
/**
32
 * Class Layout
33
 *
34
 * @package Seboettg\CiteProc\Rendering
35
 *
36
 * @author Sebastian Böttger <[email protected]>
37
 */
38
class Layout implements Rendering
39
{
40
    private static $numberOfCitedItems = 0;
41
42
    use AffixesTrait,
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\Layout.
Loading history...
43
        FormattingTrait,
44
        DelimiterTrait,
45
        ConsecutivePunctuationCharacterTrait;
46
47
    /**
48
     * @var ArrayList
49
     */
50
    private $children;
51
52
    /**
53
     * When used within cs:citation, the delimiter attribute may be used to specify a delimiter for cites within a
54
     * citation.
55
     *
56
     * @var string
57
     */
58
    private $delimiter = "";
59
60
61
    private $parent;
62
63
    /**
64
     * @param  DataList  $data
65
     * @param  MapInterface $citationItems
66
     * @return string|array
67
     */
68
    public function render(DataList $data, $citationItems)
69
    {
70
        $ret = "";
71
        $sorting = CiteProc::getContext()->getSorting();
72
        if (!empty($sorting)) {
73
            CiteProc::getContext()->setRenderingState(RenderingState::SORTING());
74
            $sorting->sort($data);
75
            CiteProc::getContext()->setRenderingState(RenderingState::RENDERING());
76
        }
77
78
        if (CiteProc::getContext()->isModeBibliography()) {
79
            foreach ($data as $citationNumber => $item) {
80
                ++self::$numberOfCitedItems;
81
                CiteProc::getContext()->getResults()->add(
82
                    $this->wrapBibEntry($item, $this->renderSingle($item, $citationNumber))
83
                );
84
            }
85
            $ret .= implode($this->delimiter, CiteProc::getContext()->getResults()->toArray());
86
            $ret = StringHelper::clearApostrophes($ret);
87
            return "<div class=\"csl-bib-body\">".$ret."\n</div>";
88
        } elseif (CiteProc::getContext()->isModeCitation()) {
89
            if ($citationItems->isNotEmpty()) { //is there a filter for specific citations?
90
                if ($this->isGroupedCitations($citationItems)) { //if citation items grouped?
91
                    return $this->renderGroupedCitations($data, $citationItems);
92
                } else {
93
                    $data = $this->filterCitationItems($data, $citationItems);
94
                    $ret = $this->renderCitations($data, $ret);
95
                }
96
            } else {
97
                $ret = $this->renderCitations($data, $ret);
98
            }
99
        }
100
        $ret = StringHelper::clearApostrophes($ret);
101
        return $this->addAffixes($ret);
102
    }
103
104
    /**
105
     * @param  SimpleXMLElement $node
106
     * @param  StyleElement     $parent
107
     * @throws InvalidStylesheetException
108
     */
109
    public function __construct($node, $parent)
110
    {
111
        $this->parent = $parent;
112
        self::$numberOfCitedItems = 0;
113
        $this->children = emptyList();
0 ignored issues
show
Documentation Bug introduced by
It seems like emptyList() of type anonymous//vendor/seboet...c/Lists/Functions.php$0 is incompatible with the declared type Seboettg\Collection\ArrayList of property $children.

Our type inference engine has found an assignment to a property that is incompatible with the declared type of that property.

Either this assignment is in error or the assigned type should be added to the documentation/type hint for that property..

Loading history...
114
        foreach ($node->children() as $child) {
115
            $this->children->add(Factory::create($child, $this));
116
        }
117
        $this->initDelimiterAttributes($node);
118
        $this->initAffixesAttributes($node);
119
        $this->initFormattingAttributes($node);
120
    }
121
122
    /**
123
     * @param  $data
124
     * @param  int|null $citationNumber
125
     * @return string
126
     */
127
    private function renderSingle($data, $citationNumber = null)
128
    {
129
        $bibliographyOptions = CiteProc::getContext()->getBibliographySpecificOptions();
130
        $inMargin = [];
131
        $margin = [];
132
        foreach ($this->children as $key => $child) {
133
            $rendered = $child->render($data, $citationNumber);
134
            $this->getChildrenAffixesAndDelimiter($child);
135
            if (CiteProc::getContext()->isModeBibliography()
136
                && $bibliographyOptions->getSecondFieldAlign() === "flush"
137
            ) {
138
                if ($key === 0 && !empty($rendered)) {
139
                    $inMargin[] = $rendered;
140
                } else {
141
                    $margin[] = $rendered;
142
                }
143
            } else {
144
                $inMargin[] = $rendered;
145
            }
146
        }
147
        $inMargin = array_filter($inMargin);
148
        $margin = array_filter($margin);
149
        if (!empty($inMargin) && !empty($margin) && CiteProc::getContext()->isModeBibliography()) {
150
            $leftMargin = $this->removeConsecutiveChars($this->htmlentities($this->format(implode("", $inMargin))));
151
            $rightInline = $this->removeConsecutiveChars(
152
                $this->htmlentities($this->format(implode("", $margin))).
153
                $this->suffix
154
            );
155
            $res  = '<div class="csl-left-margin">' . trim($leftMargin) . '</div>';
156
            $res .= '<div class="csl-right-inline">' . trim($rightInline) . '</div>';
157
            return $res;
158
        } elseif (!empty($inMargin)) {
159
            $res = $this->format(implode("", $inMargin));
160
            return $this->htmlentities($this->removeConsecutiveChars($res));
161
        }
162
        return "";
163
    }
164
165
    /**
166
     * @return int
167
     */
168
    public static function getNumberOfCitedItems()
169
    {
170
        return self::$numberOfCitedItems;
171
    }
172
173
    /**
174
     * @param  stdClass $dataItem
175
     * @param  string   $value
176
     * @return string
177
     */
178
    private function wrapBibEntry($dataItem, $value)
179
    {
180
        $value = $this->addAffixes($value);
181
        return "\n  ".
182
            "<div class=\"csl-entry\">" .
183
            $renderedItem = CiteProcHelper::applyAdditionMarkupFunction($dataItem, "csl-entry", $value) .
0 ignored issues
show
Unused Code introduced by
The assignment to $renderedItem is dead and can be removed.
Loading history...
184
            "</div>";
185
    }
186
187
    /**
188
     * @param  string $text
189
     * @return string
190
     */
191
    private function htmlentities($text)
192
    {
193
        $text = preg_replace("/(.*)&([^#38|amp];.*)/u", "$1&#38;$2", $text);
194
        return $text;
195
    }
196
197
    /**
198
     * @param  $data
199
     * @param  $ret
200
     * @return string
201
     */
202
    private function renderCitations($data, $ret)
203
    {
204
        CiteProc::getContext()->getResults()->replace([]);
205
        foreach ($data as $citationNumber => $item) {
206
            $renderedItem = $this->renderSingle($item, $citationNumber);
207
            $renderedItem = CiteProcHelper::applyAdditionMarkupFunction($item, "csl-entry", $renderedItem);
208
            CiteProc::getContext()->getResults()->add($renderedItem);
209
            CiteProc::getContext()->appendCitedItem($item);
210
        }
211
        $ret .= implode($this->delimiter, CiteProc::getContext()->getResults()->toArray());
212
        return $ret;
213
    }
214
215
    private function filterCitationItems(DataList $data, MapInterface $citationItems): DataList
216
    {
217
        $newDataList = new DataList();
218
        $newDataList->setArray($data
219
            ->filter(fn ($dataItem) => $citationItems->containsValue($dataItem->id))
220
            ->map(function ($dataItem) use ($citationItems) {
221
                    $dataItem->citationNumber = $citationItems
222
                        ->filter(fn ($_, $citationItem) => $citationItem === $dataItem->id)
223
                        ->getKeys()
224
                        ->first();
225
                    return $dataItem;
226
            })
227
            ->toArray());
228
        return $newDataList;
229
    }
230
231
    /**
232
     * @param  MapInterface $citationItems
233
     * @return bool
234
     */
235
    private function isGroupedCitations(MapInterface $citationItems): bool
236
    {
237
        $firstItem = array_values($citationItems->toArray())[0];
238
        if (is_array($firstItem)) {
239
            return true;
240
        }
241
        return false;
242
    }
243
244
    private function renderGroupedCitations(DataList $data, MapInterface $citationItems)
245
    {
246
        $group = [];
247
        foreach ($citationItems as $citationItemGroup) {
248
            $data_ = $this->filterCitationItems($data, $citationItemGroup);
249
            CiteProc::getContext()->setCitationData($data_);
250
            $group[] = $this->addAffixes(StringHelper::clearApostrophes($this->renderCitations($data_, "")));
251
        }
252
        if (CiteProc::getContext()->isCitationsAsArray()) {
253
            return $group;
254
        }
255
        return implode("\n", $group);
256
    }
257
}
258