Completed
Pull Request — master (#10)
by Steve
04:31
created

HtmlSaxDiffOutput::addAttributes()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 2
Code Lines 0

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 1
CRAP Score 1

Importance

Changes 0
Metric Value
cc 1
eloc 0
nc 1
nop 2
dl 0
loc 2
ccs 1
cts 1
cp 1
crap 1
rs 10
c 0
b 0
f 0
1
<?php
2
/**
3
 * (c) Steve Nebes <[email protected]>
4
 *
5
 * For the full copyright and license information, please view the LICENSE
6
 * file that was distributed with this source code.
7
 */
8
9
declare(strict_types=1);
10
11
namespace DaisyDiff\Html;
12
13
use DaisyDiff\Html\Dom\ImageNode;
14
use DaisyDiff\Html\Dom\TagNode;
15
use DaisyDiff\Html\Dom\TextNode;
16
use DaisyDiff\Html\Modification\Modification;
17
use DaisyDiff\Html\Modification\ModificationType;
18
use DaisyDiff\Output\DiffOutputInterface;
19
use DaisyDiff\Xml\ContentHandlerInterface;
20
21
/**
22
 * Takes a branch root and creates an HTML file for it.
23
 *
24
 * @internal
25
 */
26
class HtmlSaxDiffOutput implements DiffOutputInterface
27
{
28
    /** @var ContentHandlerInterface */
29
    private $handler;
30
31
    /**
32
     * @param ContentHandlerInterface $handler
33
     */
34
    public function __construct(ContentHandlerInterface $handler)
35
    {
36 23
        $this->handler = $handler;
37
    }
38 23
39 23
    /**
40 23
     * @param TagNode $node
41
     */
42
    public function generateOutput(TagNode $node): void
43
    {
44
        if ('img' !== $node->getQName() && 'body' !== $node->getQName()) {
45 17
            $this->handler->startElement($node->getQName(), $node->getAttributes());
46
        }
47 17
48 17
        $newStarted = false;
49
        $remStarted = false;
50
        $changeStarted = false;
51 17
        $conflictStarted = false;
52 17
        $changeText = '';
53 17
54 17
        foreach ($node as $child) {
55 17
            if ($child instanceof TagNode) {
56
                if ($newStarted) {
57 17
                    $this->handler->endElement('ins');
58 17
                    $newStarted = false;
59 16
                } elseif ($changeStarted) {
60
                    $this->handler->endElement('span');
61
                    $changeStarted = false;
62 16
                } elseif ($remStarted) {
63 1
                    $this->handler->endElement('del');
64 1
                    $remStarted = false;
65 16
                } elseif ($conflictStarted) {
66
                    $this->handler->endElement('span');
67
                    $conflictStarted = false;
68 16
                }
69
70
                $this->generateOutput($child);
71
            } elseif ($child instanceof TextNode) {
72
                $mod = $child->getModification();
73 16
74 17
                if ($newStarted && ($mod->getOutputType() !== ModificationType::ADDED || $mod->isFirstOfId())) {
75 17
                    $this->handler->endElement('ins');
76
                    $newStarted = false;
77 17
                } elseif (
78 8
                    $changeStarted && (
79 8
                        $mod->getOutputType() !== ModificationType::CHANGED ||
80 17
                        $mod->getChanges() !== $changeText ||
81
                        $mod->isFirstOfId()
82 2
                    )
83 2
                ) {
84 17
                    $this->handler->endElement('span');
85 2
                    $changeStarted = false;
86 2
                } elseif ($remStarted && ($mod->getOutputType() !== ModificationType::REMOVED || $mod->isFirstOfId())) {
87 17
                    $this->handler->endElement('del');
88 4
                    $remStarted = false;
89 4
                } elseif (
90 17
                    $conflictStarted &&
91 17
                    ($mod->getOutputType() !== ModificationType::CONFLICT || $mod->isFirstOfId())
92 1
                ) {
93 1
                    $this->handler->endElement('span');
94
                    $conflictStarted = false;
95
                }
96
97 17
                // No else because a removed part can just be closed and a new part can start.
98 11
                if (!$newStarted && $mod->getOutputType() === ModificationType::ADDED) {
99
                    $attrs = ['class' => 'diff-html-added'];
100 11
                    $this->handler->startElement('ins', $attrs);
101 11
                    $newStarted = true;
102
                } elseif (!$changeStarted && $mod->getOutputType() === ModificationType::CHANGED) {
103 11
                    $attrs = ['class' => 'diff-html-changed'];
104 17
                    $this->handler->startElement('span', $attrs);
105 3
                    $changeStarted = true;
106
                    $changeText = $mod->getChanges();
107 3
                } elseif (!$remStarted && $mod->getOutputType() === ModificationType::REMOVED) {
108 3
                    $attrs = ['class' => 'diff-html-removed'];
109
                    $this->handler->startElement('del', $attrs);
110 3
                    $remStarted = true;
111 3
                } elseif (!$conflictStarted && $mod->getOutputType() === ModificationType::CONFLICT) {
112 16
                    $attrs = ['class' => 'diff-html-conflict'];
113 6
                    $this->handler->startElement('span', $attrs);
114
                    $conflictStarted = true;
115 6
                }
116 6
117
                if ($child instanceof ImageNode) {
118 6
                    $this->writeImage($child);
119 15
                } else {
120 3
                    $this->handler->characters($child->getText());
121
                }
122 3
            }
123 3
        }
124
125 3
        if ($newStarted) {
126
            $this->handler->endElement('ins');
127
        } elseif ($changeStarted) {
128 17
            $this->handler->endElement('span');
129 5
        } elseif ($remStarted) {
130
            $this->handler->endElement('del');
131 17
        } elseif ($conflictStarted) {
132
            $this->handler->endElement('span');
133
        }
134
135
        if ('img' !== $node->getQName() && 'body' !== $node->getQName()) {
136 17
            $this->handler->endElement($node->getQName());
137 7
        }
138 17
    }
139 3
140 17
    /**
141 4
     * @param ImageNode $imageNode
142 16
     */
143 3
    private function writeImage(ImageNode $imageNode): void
144
    {
145
        $attrs = $imageNode->getAttributes();
146 17
147 17
        if ($imageNode->getModification()->getOutputType() === ModificationType::REMOVED) {
148
            $attrs['changeType'] = 'diff-removed-image';
149 17
        } elseif ($imageNode->getModification()->getOutputType() === ModificationType::ADDED) {
150
            $attrs['changeType'] = 'diff-added-image';
151
        } elseif ($imageNode->getModification()->getOutputType() === ModificationType::CONFLICT) {
152
            $attrs['changeType'] = 'diff-conflict-image';
153
        }
154 5
155
        $this->handler->startElement('img', $attrs);
156 5
        $this->handler->endElement('img');
157
    }
158
}
159