Provide source-backwards compatibility by making QAtomicInt usage optional
[vng.git] / hunks / Hunk.cpp
blobd11318bbf4594a99d928a6642219af8446ffc2b0
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 {
25 public:
26 SubHunk() : start(-1), length(0), accepted(Vng::Undecided), added(0), removed(0) { }
27 int start, length;
28 Vng::Acceptance accepted;
29 int added, removed;
30 int lineNumber;
33 class Hunk::Private {
34 public:
35 Private() : lineNumber(-1), ref(1), linesBefore(0), linesAfter(0)
37 SubHunk *sh = new SubHunk();
38 subhunks.append(sh);
41 ~Private() {
42 qDeleteAll(subhunks);
44 QByteArray header;
45 int lineNumber;
46 QByteArray diff;
47 #if QT_VERSION >= 0x040400
48 QAtomicInt ref;
49 #else
50 int ref;
51 #endif
52 int linesBefore, linesAfter;
54 QList<SubHunk *> subhunks;
56 QByteArray patchForAccepted(bool inverse);
59 Hunk::Hunk()
60 : d(new Private())
64 Hunk::Hunk(const Hunk &other)
65 : d(other.d)
67 #if QT_VERSION >= 0x040400
68 d->ref.ref();
69 #else
70 d->ref++;
71 #endif
74 Hunk::~Hunk()
76 #if QT_VERSION >= 0x040400
77 if (!d->ref.deref())
78 #else
79 if (--d->ref == 0)
80 #endif
81 delete d;
84 void Hunk::addLine(const QByteArray &line)
86 if (line.size() == 0)
87 return;
88 if(d->header.isEmpty()) {
89 d->header = line;
90 QString header(line);
91 QRegExp regexp("^@@ -[\\d,]+ \\+(\\d+)\\b");
92 if (regexp.indexIn(header) != -1)
93 d->lineNumber = regexp.cap(1).toInt();
94 return;
96 if (line[0] == '\\') { // "\ No newline at end of file"
97 if (d->diff.size()) {
98 // check if we removed something from the last hunk and adjust its size
99 SubHunk *last = d->subhunks[d->subhunks.count()-1];
100 if (last->start + last->length == d->diff.size())
101 --last->length;
102 d->diff.chop(1); // TODO should that be 2 on Windows?
104 return;
106 else if (line[0] != ' ') {
107 SubHunk *last = d->subhunks[d->subhunks.count()-1];
108 if (last->start < 0) {
109 last->start = d->diff.size();
110 last->lineNumber = d->lineNumber + qMax(d->linesBefore, d->linesAfter);
112 else if (last->start + last->length < d->diff.size()) {
113 last = new SubHunk();
114 d->subhunks.append(last);
115 last->start = d->diff.size();
116 last->lineNumber = d->lineNumber + qMax(d->linesBefore, d->linesAfter);
118 last->length+=line.length();
119 if (line[0] == '-') {
120 last->removed++;
121 d->linesBefore++;
123 else {
124 last->added++;
125 d->linesAfter++;
128 else {
129 d->linesBefore++;
130 d->linesAfter++;
132 d->diff.append(line);
135 Hunk & Hunk::operator=(const Hunk &other)
137 #if QT_VERSION >= 0x040400
138 other.d->ref.ref();
139 if (! d->ref.deref())
140 #else
141 other.d->ref++;
142 if (--d->ref == 0)
143 #endif
144 delete d;
145 d = other.d;
146 return *this;
149 int Hunk::lineNumber() const
151 return d->lineNumber;
154 int Hunk::lineNumber(int subHunk) const
156 Q_ASSERT(subHunk < d->subhunks.count());
157 SubHunk *sh = d->subhunks[subHunk];
158 return sh->lineNumber;
161 QByteArray Hunk::patch() const
163 return d->diff;
166 bool Hunk::isEmpty() const
168 return d->diff.isEmpty();
171 Vng::Acceptance Hunk::acceptance() const
173 Vng::Acceptance a = Vng::Undecided;
174 for (int i = 0; i < d->subhunks.count(); i++) {
175 SubHunk *sh = d->subhunks[i];
176 if (a != sh->accepted) {
177 if (a == Vng::Undecided)
178 a = sh->accepted;
179 else
180 return Vng::MixedAcceptance;
183 return a;
186 void Hunk::setAcceptance(Vng::Acceptance acceptance)
188 foreach(SubHunk *sh, d->subhunks)
189 sh->accepted = acceptance;
192 Vng::Acceptance Hunk::acceptance(int subHunk) const
194 Q_ASSERT(subHunk < d->subhunks.count());
195 SubHunk *sh = d->subhunks[subHunk];
196 return sh->accepted;
199 void Hunk::setAcceptance(int subhunk, Vng::Acceptance accepted)
201 Q_ASSERT(subhunk < d->subhunks.count());
202 SubHunk *sh = d->subhunks[subhunk];
203 sh->accepted = accepted;
206 QByteArray Hunk::header() const
208 return d->header;
211 QByteArray Hunk::subHunk(int index) const
213 if (d->subhunks.count() == 0)
214 return QByteArray();
215 Q_ASSERT(index >= 0);
216 Q_ASSERT(index < d->subhunks.count());
217 SubHunk *sh = d->subhunks[index];
218 return d->diff.mid(sh->start, sh->length);
221 int Hunk::subHunkCount() const
223 return d->subhunks.count();
226 QByteArray Hunk::rejectedPatch() const
228 return d->patchForAccepted(true);
231 QByteArray Hunk::acceptedPatch() const
233 return d->patchForAccepted(false);
236 QByteArray Hunk::Private::patchForAccepted(bool inverse)
238 QByteArray patch;
239 QDataStream out (&patch, QIODevice::WriteOnly);
240 const char *myDiff = diff.data();
241 const char *space = " ";
242 int offsetInDiff = 0; // position in diff
243 int before = linesBefore; // for the header
244 foreach(SubHunk *sh, subhunks) {
245 out.writeRawData(myDiff + offsetInDiff, sh->start - offsetInDiff); // write until start
246 if (!inverse && sh->accepted == Vng::Accepted || inverse && sh->accepted != Vng::Accepted) {
247 out.writeRawData(myDiff + sh->start, sh->length);
249 else {
250 const int end = sh->start + sh->length;
251 bool startOfLine = true;
252 bool skipLine;
253 int lastLineStart = 0;
254 for (int x = sh->start; x < end; x++) {
255 if (startOfLine) {
256 startOfLine = false;
257 skipLine = myDiff[x] == '-';
258 if (! skipLine) {
259 out.writeRawData(space, 1);
260 lastLineStart = x + 1;
262 continue;
264 if (myDiff[x] == '\n') {
265 startOfLine = true;
266 if (! skipLine)
267 out.writeRawData(myDiff + lastLineStart, x - lastLineStart + 1);
270 before += sh->added - sh->removed;
272 offsetInDiff = sh->start + sh->length;
274 Q_ASSERT(offsetInDiff <= diff.size());
275 out.writeRawData(myDiff + offsetInDiff, diff.size() - offsetInDiff);
276 if (patch[patch.size() -1] != '\n') {
277 const char *noNewLine = "\n\\ No newline at end of file\n";;
278 out.writeRawData(noNewLine, strlen(noNewLine));
281 // create header
282 QString header = "@@ -"+ QString::number(lineNumber) +","+ QString::number(before)
283 +" +"+ QString::number(lineNumber) +","+ QString::number(linesAfter) + " @@\n";
285 QByteArray answer;
286 answer.append(header);
287 answer.append(patch);
288 return answer;
291 int Hunk::linesAdded() const
293 int total = 0;
294 foreach(SubHunk *sh, d->subhunks)
295 total += sh->added;
296 return total;
299 int Hunk::linesRemoved() const
301 int total = 0;
302 foreach(SubHunk *sh, d->subhunks)
303 total += sh->removed;
304 return total;