Passed
Push — master ( 428df7...389478 )
by Sebastian
08:27 queued 11s
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 = [])
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