Passed
Push — master ( 428df7...389478 )
by Sebastian
08:27 queued 11s
created

Layout   A

Complexity

Total Complexity 32

Size/Duplication

Total Lines 228
Duplicated Lines 0 %

Test Coverage

Coverage 96.91%

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 101
c 1
b 0
f 0
dl 0
loc 228
ccs 94
cts 97
cp 0.9691
rs 9.84
wmc 32

10 Methods

Rating   Name   Duplication   Size   Complexity  
A __construct() 0 11 2
A renderCitations() 0 10 2
A getNumberOfCitedItems() 0 3 1
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 33 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 = [])
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) {++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 98
        } elseif (CiteProc::getContext()->isModeCitation()) {
104 98
            if ($citationItems->count() > 0) { //is there a filter for specific citations?
105 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

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

106
                    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...
107
                } else {
108 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

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