Passed
Push — new-api ( 5677f6...2a03a6 )
by Sebastian
04:12
created

Layout::setChildren()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 2
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 1
dl 0
loc 3
ccs 2
cts 2
cp 1
crap 1
rs 10
c 0
b 0
f 0
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\Config\RenderingState;
15
use Seboettg\CiteProc\Exception\InvalidStylesheetException;
16
use Seboettg\CiteProc\Styles\ConsecutivePunctuationCharacterTrait;
17
use Seboettg\CiteProc\Styles\StylesRenderer;
18
use Seboettg\CiteProc\Util\CiteProcHelper;
19
use Seboettg\CiteProc\Util\Factory;
20
use Seboettg\CiteProc\Util\StringHelper;
21
use Seboettg\Collection\ArrayList;
22
use SimpleXMLElement;
23
use stdClass;
24
25
/**
26
 * Class Layout
27
 *
28
 * @package Seboettg\CiteProc\Rendering
29
 *
30
 * @author Sebastian Böttger <[email protected]>
31
 */
32
class Layout implements Rendering
33
{
34
    use ConsecutivePunctuationCharacterTrait;
35
36
    private static $numberOfCitedItems = 0;
37
38
    /**
39
     * @var ArrayList
40
     */
41
    private $children;
42
43
    /**
44
     * When used within cs:citation, the delimiter attribute may be used to specify a delimiter for cites within a
45
     * citation.
46
     *
47
     * @var string
48
     */
49
    private $delimiter = "";
50
51
52
    private $parent;
53
54
    /**
55
     * @var StylesRenderer
56
     */
57
    private $stylesRenderer;
58
59
    /**
60
     * @param SimpleXMLElement $node
61
     * @return Layout
62
     * @throws InvalidStylesheetException
63
     */
64 171
    public static function factory(SimpleXMLElement $node): Layout
65
    {
66 171
        $children = new ArrayList();
67 171
        $layout = new Layout();
68 171
        foreach ($node->children() as $csChild) {
69 171
            $child = Factory::create($csChild);
70 171
            $child->setParent($layout);
71 171
            $children->append($child);
72
        }
73 171
        $stylesRenderer = StylesRenderer::factory($node);
74 171
        $layout->setChildren($children);
75 171
        $layout->setStylesRenderer($stylesRenderer);
76 171
        $layout->setDelimiter((string)$node->attributes()['delimiter']);
77 171
        return $layout;
78
    }
79
80 171
    public function __construct()
81
    {
82 171
        $this->children = new ArrayList();
83 171
    }
84
85
    /**
86
     * @param  array|DataList  $data
87
     * @param  array|ArrayList $citationItems
88
     * @return string|array
89
     */
90 167
    public function render($data, $citationItems = [])
0 ignored issues
show
Coding Style introduced by
Incorrect spacing between argument "$citationItems" and equals sign; expected 0 but found 1
Loading history...
Coding Style introduced by
Incorrect spacing between default value and equals sign for argument "$citationItems"; expected 0 but found 1
Loading history...
91
    {
92 167
        $ret = "";
93 167
        $sorting = CiteProc::getContext()->getSorting();
94 167
        if (!empty($sorting)) {
95 56
            CiteProc::getContext()->setRenderingState(new RenderingState("sorting"));
96 56
            $sorting->sort($data);
97 56
            CiteProc::getContext()->setRenderingState(new RenderingState("rendering"));
98
        }
99
100 167
        if (CiteProc::getContext()->isModeBibliography()) {
101 79
            foreach ($data as $citationNumber => $item) {
102 79
                ++self::$numberOfCitedItems;
103 79
                CiteProc::getContext()->getResults()->append(
104 79
                    $this->wrapBibEntry($item, $this->renderSingle($item, $citationNumber))
105
                );
106
            }
107 79
            $ret .= implode($this->delimiter, CiteProc::getContext()->getResults()->toArray());
108 79
            $ret = StringHelper::clearApostrophes($ret);
109 79
            return "<div class=\"csl-bib-body\">".$ret."\n</div>";
110 103
        } elseif (CiteProc::getContext()->isModeCitation()) {
111 103
            if ($citationItems->count() > 0) { //is there a filter for specific citations?
112 9
                if ($this->isGroupedCitations($citationItems)) { //if citation items grouped?
0 ignored issues
show
Bug introduced by
It seems like $citationItems can also be of type array; however, parameter $citationItems of Seboettg\CiteProc\Render...t::isGroupedCitations() does only seem to accept Seboettg\Collection\ArrayList, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

112
                if ($this->isGroupedCitations(/** @scrutinizer ignore-type */ $citationItems)) { //if citation items grouped?
Loading history...
113 1
                    return $this->renderGroupedCitations($data, $citationItems);
0 ignored issues
show
Bug introduced by
It seems like $citationItems can also be of type array; however, parameter $citationItems of Seboettg\CiteProc\Render...enderGroupedCitations() does only seem to accept Seboettg\Collection\ArrayList, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

113
                    return $this->renderGroupedCitations($data, /** @scrutinizer ignore-type */ $citationItems);
Loading history...
114
                } else {
115 8
                    $data = $this->filterCitationItems($data, $citationItems);
0 ignored issues
show
Bug introduced by
It seems like $citationItems can also be of type array; however, parameter $citationItems of Seboettg\CiteProc\Render...::filterCitationItems() does only seem to accept Seboettg\Collection\ArrayList, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

115
                    $data = $this->filterCitationItems($data, /** @scrutinizer ignore-type */ $citationItems);
Loading history...
116 8
                    $ret = $this->renderCitations($data, $ret);
117
                }
118
            } else {
119 95
                $ret = $this->renderCitations($data, $ret);
120
            }
121
        }
122 102
        $ret = StringHelper::clearApostrophes($ret);
123 102
        return $this->stylesRenderer->renderAffixes($ret);
124
    }
125
126
    /**
127
     * @param  $data
128
     * @param  int|null $citationNumber
129
     * @return string
130
     */
131 167
    private function renderSingle($data, $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...
132
    {
133 167
        $bibliographyOptions = CiteProc::getContext()->getBibliographySpecificOptions();
134 167
        $inMargin = [];
135 167
        $margin = [];
136 167
        foreach ($this->children as $key => $child) {
137 167
            $rendered = $child->render($data, $citationNumber);
138 167
            $this->getChildrenAffixesAndDelimiter($child);
139 167
            if (CiteProc::getContext()->isModeBibliography()
140 167
                && $bibliographyOptions->getSecondFieldAlign() === "flush"
141
            ) {
142 10
                if ($key === 0 && !empty($rendered)) {
143 10
                    $inMargin[] = $rendered;
144
                } else {
145 10
                    $margin[] = $rendered;
146
                }
147
            } else {
148 167
                $inMargin[] = $rendered;
149
            }
150
        }
151 167
        $inMargin = array_filter($inMargin);
152 167
        $margin = array_filter($margin);
153 167
        if (!empty($inMargin) && !empty($margin) && CiteProc::getContext()->isModeBibliography()) {
154 9
            $leftMargin = $this->removeConsecutiveChars(
155 9
                $this->htmlentities($this->stylesRenderer->renderFormatting(implode("", $inMargin)))
156
            );
157 9
            $result = $this->htmlentities($this->stylesRenderer->renderFormatting(implode("", $margin)));
158 9
            $result = rtrim($result, $this->stylesRenderer->getAffixes()->getSuffix()) .
159 9
                $this->stylesRenderer->getAffixes()->getSuffix();
160 9
            $rightInline = $this->removeConsecutiveChars($result);
161 9
            $res  = '<div class="csl-left-margin">' . trim($leftMargin) . '</div>';
162 9
            $res .= '<div class="csl-right-inline">' . trim($rightInline) . '</div>';
163 9
            return $res;
164 159
        } elseif (!empty($inMargin)) {
165 159
            $res = $this->stylesRenderer->renderFormatting(implode("", $inMargin));
166 159
            return $this->htmlentities($this->removeConsecutiveChars($res));
167
        }
168
        return "";
169
    }
170
171
    /**
172
     * @return int
173
     */
174
    public static function getNumberOfCitedItems(): int
175
    {
176
        return self::$numberOfCitedItems;
177
    }
178
179
    /**
180
     * @param  stdClass $dataItem
181
     * @param  string   $value
182
     * @return string
183
     */
184 79
    private function wrapBibEntry($dataItem, $value): string
185
    {
186 79
        $value = $this->stylesRenderer->renderAffixes($value);
187
        return "\n  ".
188
            "<div class=\"csl-entry\">" .
189 79
            $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...
190 79
            "</div>";
191
    }
192
193
    /**
194
     * @param  string $text
195
     * @return string
196
     */
197 167
    private function htmlentities($text)
198
    {
199 167
        $text = preg_replace("/(.*)&([^#38|amp];.*)/u", "$1&#38;$2", $text);
200 167
        return $text;
201
    }
202
203
    /**
204
     * @param  $data
205
     * @param  $ret
206
     * @return string
207
     */
208 103
    private function renderCitations($data, $ret)
209
    {
210 103
        CiteProc::getContext()->getResults()->replace([]);
211 103
        foreach ($data as $citationNumber => $item) {
212 103
            $renderedItem = $this->renderSingle($item, $citationNumber);
213 103
            $renderedItem = CiteProcHelper::applyAdditionMarkupFunction($item, "csl-entry", $renderedItem);
214 103
            CiteProc::getContext()->getResults()->append($renderedItem);
215 103
            CiteProc::getContext()->appendCitedItem($item);
216
        }
217 103
        $ret .= implode($this->delimiter, CiteProc::getContext()->getResults()->toArray());
218 103
        return $ret;
219
    }
220
221
    /**
222
     * @param  DataList  $data
223
     * @param  ArrayList $citationItems
224
     * @return mixed
225
     */
226 9
    private function filterCitationItems($data, $citationItems)
227
    {
228 9
        $arr = $data->toArray();
229
230
        $arr_ = array_filter($arr, function ($dataItem) use ($citationItems) {
231 9
            foreach ($citationItems as $citationItem) {
232 9
                if ($dataItem->id === $citationItem->id) {
233 9
                    return true;
234
                }
235
            }
236 3
            return false;
237 9
        });
238
239 9
        return $data->replace($arr_);
240
    }
241
242
    /**
243
     * @param  ArrayList $citationItems
244
     * @return bool
245
     */
246 9
    private function isGroupedCitations(ArrayList $citationItems)
247
    {
248 9
        $firstItem = array_values($citationItems->toArray())[0];
249 9
        if (is_array($firstItem)) {
250 1
            return true;
251
        }
252 8
        return false;
253
    }
254
255
    /**
256
     * @param  DataList  $data
257
     * @param  ArrayList $citationItems
258
     * @return array|string
259
     */
260 1
    private function renderGroupedCitations($data, $citationItems)
261
    {
262 1
        $group = [];
263 1
        foreach ($citationItems as $citationItemGroup) {
264 1
            $data_ = $this->filterCitationItems(clone $data, $citationItemGroup);
265 1
            CiteProc::getContext()->setCitationData($data_);
266 1
            $group[] = $this->stylesRenderer->renderAffixes(
267 1
                StringHelper::clearApostrophes($this->renderCitations($data_, ""))
268
            );
269
        }
270 1
        if (CiteProc::getContext()->isCitationsAsArray()) {
271 1
            return $group;
272
        }
273
        return implode("\n", $group);
274
    }
275
276 171
    public function setChildren(ArrayList $children)
277
    {
278 171
        $this->children = $children;
279 171
    }
280
281 171
    public function setStylesRenderer(StylesRenderer $stylesRenderer)
282
    {
283 171
        $this->stylesRenderer = $stylesRenderer;
284 171
    }
285
286 171
    public function setDelimiter(string $delimiter)
287
    {
288 171
        $this->delimiter = $delimiter;
289 171
    }
290
291 171
    public function setParent($parent)
292
    {
293 171
        $this->parent = $parent;
294 171
    }
295
}
296