Passed
Push — feature/locators-issue-82 ( d78ed3...9d87a1 )
by Sebastian
06:58
created

Layout   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 228
Duplicated Lines 0 %

Test Coverage

Coverage 95.96%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 103
dl 0
loc 228
ccs 95
cts 99
cp 0.9596
rs 9.84
c 1
b 0
f 0
wmc 32

10 Methods

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

105
                if ($this->isGroupedCitations(/** @scrutinizer ignore-type */ $citationItems)) { //if citation items grouped?
Loading history...
106 1
                    return $this->renderGroupedCitations($data, $citationItems);
0 ignored issues
show
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...
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

106
                    return $this->renderGroupedCitations($data, /** @scrutinizer ignore-type */ $citationItems);
Loading history...
107
                } else {
108 7
                    $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

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