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

Layout::renderSingle()   B

Complexity

Conditions 10
Paths 12

Size

Total Lines 37
Code Lines 26

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 24
CRAP Score 10.0064

Importance

Changes 1
Bugs 0 Features 0
Metric Value
cc 10
eloc 26
c 1
b 0
f 0
nc 12
nop 2
dl 0
loc 37
ccs 24
cts 25
cp 0.96
crap 10.0064
rs 7.6666

How to fix   Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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