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

Layout   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 229
Duplicated Lines 0 %

Test Coverage

Coverage 96.94%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 102
c 1
b 0
f 0
dl 0
loc 229
ccs 95
cts 98
cp 0.9694
rs 9.84
wmc 32

10 Methods

Rating   Name   Duplication   Size   Complexity  
A renderCitations() 0 10 2
A getNumberOfCitedItems() 0 3 1
A __construct() 0 11 2
A isGroupedCitations() 0 7 2
A renderGroupedCitations() 0 12 3
B renderSingle() 0 37 10
A wrapBibEntry() 0 7 1
A htmlentities() 0 4 1
A filterCitationItems() 0 14 3
B render() 0 34 7
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 SimpleXMLElement;
26
use stdClass;
27
28
/**
29
 * Class Layout
30
 *
31
 * @package Seboettg\CiteProc\Rendering
32
 *
33
 * @author Sebastian Böttger <[email protected]>
34
 */
35
class Layout implements Rendering
36
{
37
38
    private static $numberOfCitedItems = 0;
39
40
    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...
41
        FormattingTrait,
42
        DelimiterTrait,
43
        ConsecutivePunctuationCharacterTrait;
44
45
    /**
46
     * @var ArrayList
47
     */
48
    private $children;
49
50
    /**
51
     * When used within cs:citation, the delimiter attribute may be used to specify a delimiter for cites within a
52
     * citation.
53
     *
54
     * @var string
55
     */
56
    private $delimiter = "";
57
58
59
    private $parent;
60
61
    /**
62
     * @param  SimpleXMLElement $node
63
     * @param  StyleElement     $parent
64
     * @throws InvalidStylesheetException
65
     */
66 154
    public function __construct($node, $parent)
67
    {
68 154
        $this->parent = $parent;
69 154
        self::$numberOfCitedItems = 0;
70 154
        $this->children = new ArrayList();
71 154
        foreach ($node->children() as $child) {
72 154
            $this->children->append(Factory::create($child, $this));
73
        }
74 154
        $this->initDelimiterAttributes($node);
75 154
        $this->initAffixesAttributes($node);
76 154
        $this->initFormattingAttributes($node);
77 154
    }
78
79
    /**
80
     * @param  array|DataList  $data
81
     * @param  array|ArrayList $citationItems
82
     * @return string|array
83
     */
84 150
    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...
85
    {
86 150
        $ret = "";
87 150
        $sorting = CiteProc::getContext()->getSorting();
88 150
        if (!empty($sorting)) {
89 50
            CiteProc::getContext()->setRenderingState(new RenderingState("sorting"));
90 50
            $sorting->sort($data);
91 50
            CiteProc::getContext()->setRenderingState(new RenderingState("rendering"));
92
        }
93
94 150
        if (CiteProc::getContext()->isModeBibliography()) {
95 68
            foreach ($data as $citationNumber => $item) {
96 68
                ++self::$numberOfCitedItems;
97 68
                CiteProc::getContext()->getResults()->append(
98 68
                    $this->wrapBibEntry($item, $this->renderSingle($item, $citationNumber))
99
                );
100
            }
101 68
            $ret .= implode($this->delimiter, CiteProc::getContext()->getResults()->toArray());
102 68
            $ret = StringHelper::clearApostrophes($ret);
103 68
            return "<div class=\"csl-bib-body\">" . $ret . "\n</div>";
104 98
        } elseif (CiteProc::getContext()->isModeCitation()) {
105 98
            if ($citationItems->count() > 0) { //is there a filter for specific citations?
106 3
                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

106
                if ($this->isGroupedCitations(/** @scrutinizer ignore-type */ $citationItems)) { //if citation items grouped?
Loading history...
107 2
                    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

107
                    return $this->renderGroupedCitations($data, /** @scrutinizer ignore-type */ $citationItems);
Loading history...
Bug Best Practice introduced by
The expression return $this->renderGrou...($data, $citationItems) also could return the type array|string[] which is incompatible with the return type mandated by Seboettg\CiteProc\Rendering\Rendering::render() of string.
Loading history...
108
                } else {
109 1
                    $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

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