Make sure we don't ignore hidden files.
[vng.git] / src / hunks / Hunk.cpp
blob4f70741e6952f73433d28eafea550dd1055a67e6
1 /*
2 * This file is part of the vng project
3 * Copyright (C) 2008 Thomas Zander <tzander@trolltech.com>
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 #include "Hunk.h"
21 #include <QDebug>
22 #include <QRegExp>
24 class SubHunk
26 public:
27 SubHunk() : start(-1), length(0), accepted(Vng::Undecided), added(0), removed(0) { }
28 int start, length;
29 Vng::Acceptance accepted;
30 int added, removed;
31 int lineNumber;
34 class Hunk::Private
36 public:
37 Private() : lineNumber(-1), ref(1), linesBefore(0), linesAfter(0)
39 SubHunk *sh = new SubHunk();
40 subhunks.append(sh);
43 ~Private() {
44 qDeleteAll(subhunks);
46 QByteArray header;
47 int lineNumber;
48 QByteArray diff;
49 #if QT_VERSION >= 0x040400
50 QAtomicInt ref;
51 #else
52 int ref;
53 #endif
54 int linesBefore, linesAfter;
56 QList<SubHunk *> subhunks;
58 QByteArray patchForAccepted(bool inverse);
61 Hunk::Hunk()
62 : d(new Private())
66 Hunk::Hunk(const Hunk &other)
67 : d(other.d)
69 #if QT_VERSION >= 0x040400
70 d->ref.ref();
71 #else
72 d->ref++;
73 #endif
76 Hunk::~Hunk()
78 #if QT_VERSION >= 0x040400
79 if (!d->ref.deref())
80 #else
81 if (--d->ref == 0)
82 #endif
83 delete d;
86 void Hunk::addLine(const QByteArray &line)
88 if (line.size() == 0)
89 return;
90 if(d->header.isEmpty()) {
91 d->header = line;
92 QString header(line);
93 QRegExp regexp("^@@ -[\\d,]+ \\+(\\d+)\\b");
94 if (regexp.indexIn(header) != -1)
95 d->lineNumber = regexp.cap(1).toInt();
96 return;
98 if (line[0] == '\\') { // "\ No newline at end of file"
99 if (d->diff.size()) {
100 // check if we removed something from the last hunk and adjust its size
101 SubHunk *last = d->subhunks[d->subhunks.count()-1];
102 if (last->start + last->length == d->diff.size())
103 --last->length;
104 d->diff.chop(1); // TODO should that be 2 on Windows?
106 return;
108 else if (line[0] != ' ') {
109 SubHunk *last = d->subhunks[d->subhunks.count()-1];
110 if (last->start < 0) {
111 last->start = d->diff.size();
112 last->lineNumber = d->lineNumber + qMax(d->linesBefore, d->linesAfter);
114 else if (last->start + last->length < d->diff.size()) {
115 last = new SubHunk();
116 d->subhunks.append(last);
117 last->start = d->diff.size();
118 last->lineNumber = d->lineNumber + qMax(d->linesBefore, d->linesAfter);
120 last->length+=line.length();
121 if (line[0] == '-') {
122 last->removed++;
123 d->linesBefore++;
125 else {
126 last->added++;
127 d->linesAfter++;
130 else {
131 d->linesBefore++;
132 d->linesAfter++;
134 d->diff.append(line);
137 Hunk & Hunk::operator=(const Hunk &other)
139 #if QT_VERSION >= 0x040400
140 other.d->ref.ref();
141 if (! d->ref.deref())
142 #else
143 other.d->ref++;
144 if (--d->ref == 0)
145 #endif
146 delete d;
147 d = other.d;
148 return *this;
151 int Hunk::lineNumber() const
153 return d->lineNumber;
156 int Hunk::lineNumber(int subHunk) const
158 Q_ASSERT(subHunk < d->subhunks.count());
159 SubHunk *sh = d->subhunks[subHunk];
160 return sh->lineNumber;
163 QByteArray Hunk::patch() const
165 return d->diff;
168 bool Hunk::isEmpty() const
170 return d->diff.isEmpty();
173 Vng::Acceptance Hunk::acceptance() const
175 Vng::Acceptance a = Vng::Undecided;
176 for (int i = 0; i < d->subhunks.count(); i++) {
177 SubHunk *sh = d->subhunks[i];
178 if (a != sh->accepted) {
179 if (a == Vng::Undecided)
180 a = sh->accepted;
181 else
182 return Vng::MixedAcceptance;
185 return a;
188 void Hunk::setAcceptance(Vng::Acceptance acceptance)
190 foreach(SubHunk *sh, d->subhunks)
191 sh->accepted = acceptance;
194 Vng::Acceptance Hunk::acceptance(int subHunk) const
196 Q_ASSERT(subHunk < d->subhunks.count());
197 SubHunk *sh = d->subhunks[subHunk];
198 return sh->accepted;
201 void Hunk::setAcceptance(int subhunk, Vng::Acceptance accepted)
203 Q_ASSERT(subhunk < d->subhunks.count());
204 SubHunk *sh = d->subhunks[subhunk];
205 sh->accepted = accepted;
208 QByteArray Hunk::header() const
210 return d->header;
213 QByteArray Hunk::subHunk(int index) const
215 if (d->subhunks.count() == 0)
216 return QByteArray();
217 Q_ASSERT(index >= 0);
218 Q_ASSERT(index < d->subhunks.count());
219 SubHunk *sh = d->subhunks[index];
220 return d->diff.mid(sh->start, sh->length);
223 int Hunk::subHunkCount() const
225 return d->subhunks.count();
228 QByteArray Hunk::rejectedPatch() const
230 return d->patchForAccepted(true);
233 QByteArray Hunk::acceptedPatch() const
235 return d->patchForAccepted(false);
238 QByteArray Hunk::Private::patchForAccepted(bool inverse)
240 QByteArray patch;
241 QDataStream out (&patch, QIODevice::WriteOnly);
242 const char *myDiff = diff.data();
243 const char *space = " ";
244 int offsetInDiff = 0; // position in diff
245 int before = linesBefore; // for the header
246 foreach(SubHunk *sh, subhunks) {
247 out.writeRawData(myDiff + offsetInDiff, sh->start - offsetInDiff); // write until start
248 if ((!inverse && sh->accepted == Vng::Accepted) || (inverse && sh->accepted != Vng::Accepted)) {
249 out.writeRawData(myDiff + sh->start, sh->length);
251 else {
252 const int end = sh->start + sh->length;
253 bool startOfLine = true;
254 bool skipLine = false;
255 int lastLineStart = 0;
256 for (int x = sh->start; x < end; x++) {
257 if (startOfLine) {
258 startOfLine = false;
259 skipLine = (myDiff[x] == '-');
260 if (! skipLine) {
261 out.writeRawData(space, 1);
262 lastLineStart = x + 1;
264 continue;
266 if (myDiff[x] == '\n') {
267 startOfLine = true;
268 if (! skipLine)
269 out.writeRawData(myDiff + lastLineStart, x - lastLineStart + 1);
272 before += sh->added - sh->removed;
274 offsetInDiff = sh->start + sh->length;
276 Q_ASSERT(offsetInDiff <= diff.size());
277 out.writeRawData(myDiff + offsetInDiff, diff.size() - offsetInDiff);
278 if (patch[patch.size() -1] != '\n') {
279 const char *noNewLine = "\n\\ No newline at end of file\n";;
280 out.writeRawData(noNewLine, strlen(noNewLine));
283 // create header
284 QString header = "@@ -"+ QString::number(lineNumber) +","+ QString::number(before)
285 +" +"+ QString::number(lineNumber) +","+ QString::number(linesAfter) + " @@\n";
287 QByteArray answer;
288 answer.append(header.toLocal8Bit());
289 answer.append(patch);
290 return answer;
293 int Hunk::linesAdded() const
295 int total = 0;
296 foreach(SubHunk *sh, d->subhunks)
297 total += sh->added;
298 return total;
301 int Hunk::linesRemoved() const
303 int total = 0;
304 foreach(SubHunk *sh, d->subhunks)
305 total += sh->removed;
306 return total;