1 | |
2 | static class BlockDiff { |
3 | public CopyBlock asCopyBlock() { return null; } |
4 | public NewBlock asNewBlock () { return null; } |
5 | } |
6 | |
7 | static class CopyBlock extends BlockDiff { |
8 | int firstLine, lines; |
9 | |
10 | CopyBlock(int firstLine, int lines) { |
11 | this.firstLine = firstLine; |
12 | this.lines = lines; |
13 | } |
14 | |
15 | public CopyBlock asCopyBlock() { return this; } |
16 | public int getFirstLine() { return firstLine; } |
17 | public int getLines() { return lines; } |
18 | } |
19 | |
20 | static class NewBlock extends BlockDiff { |
21 | int originalStart; |
22 | List<String> contents; |
23 | |
24 | NewBlock(int originalStart, List<String> contents) { |
25 | this.originalStart = originalStart; |
26 | this.contents = contents; |
27 | } |
28 | |
29 | public NewBlock asNewBlock () { return this; } |
30 | |
31 | public int getOriginalStart() { |
32 | return originalStart; |
33 | } |
34 | |
35 | public List<String> getContents() { |
36 | return contents; |
37 | } |
38 | } |
39 | |
40 | static class ExplodedLine { |
41 | int type; |
42 | String left, right; |
43 | int leftIndex, rightIndex; |
44 | |
45 | ExplodedLine(int type, String left, String right, int leftIndex, int rightIndex) { |
46 | this.type = type; |
47 | this.left = left; |
48 | this.right = right; |
49 | this.leftIndex = leftIndex; |
50 | this.rightIndex = rightIndex; |
51 | } |
52 | |
53 | public int getType() { |
54 | return type; |
55 | } |
56 | |
57 | public String getLeft() { |
58 | return left; |
59 | } |
60 | |
61 | public String getRight() { |
62 | return right; |
63 | } |
64 | |
65 | public int getLeftIndex() { |
66 | return leftIndex; |
67 | } |
68 | |
69 | public int getRightIndex() { |
70 | return rightIndex; |
71 | } |
72 | } |
73 | |
74 | static class BlockDiffer { |
75 | public static final int IDENTICAL = 0; |
76 | public static final int DIFFERENT = 1; |
77 | public static final int LEFT_ONLY = 2; |
78 | public static final int RIGHT_ONLY = 3; |
79 | |
80 | private static void printChange(EGDiff.change change) { |
81 | if (change != null) { |
82 | System.out.println("line0="+change.line0+", line1="+change.line1 |
83 | +", inserted="+change.inserted+", deleted="+change.deleted); |
84 | printChange(change.link); |
85 | } |
86 | } |
87 | |
88 | |
89 | /** Generates the text content of a Unified-format context diff between 2 files |
90 | * (NB the 'files-changed' header must be added separately). |
91 | */ |
92 | public static List<String> generateUniDiff(List<String> fileA, List<String> fileB, int contextSize) { |
93 | EGDiff diff = new EGDiff(fileA.toArray(), fileB.toArray()); |
94 | EGDiff.change change = diff.diff_2(false); |
95 | |
96 | if (change != null) { |
97 | int inserted, deleted; |
98 | List<String> hunkLines = new ArrayList<String>(); |
99 | int cumulExtraLinesBwrtA = 0; |
100 | |
101 | // Each hunk is generated with a header |
102 | do { |
103 | int line0 = change.line0, line1 = change.line1; |
104 | int changeStart = ((line1 < line0) ? line1 : line0); |
105 | int contextStart = ((changeStart > contextSize) ? changeStart - contextSize : 0); |
106 | int headerPosn = hunkLines.size(); |
107 | |
108 | // Provide the first lines of context |
109 | for (int i = contextStart; i < changeStart; i++) |
110 | //System.out.println(" " + fileA.get(i)); |
111 | hunkLines.add(" " + fileA.get(i)); |
112 | |
113 | boolean hunkFinish = false; |
114 | // Step through each change giving the change lines and following context |
115 | do { |
116 | inserted = change.inserted; |
117 | deleted = change.deleted; |
118 | line0 = change.line0; |
119 | line1 = change.line1; |
120 | |
121 | if (line1 < line0) // An insert comes earlier |
122 | while (inserted-- > 0) |
123 | hunkLines.add("+" + fileB.get(line1++)); |
124 | while (deleted-- > 0) |
125 | hunkLines.add("-" + fileA.get(line0++)); |
126 | while (inserted-- > 0) |
127 | hunkLines.add("+" + fileB.get(line1++)); |
128 | |
129 | // Lines following are trailing context, identical in fileA and fileB |
130 | // The next change may overlap the context, so check and if so, form one hunk |
131 | EGDiff.change nextChange = change.link; |
132 | int nextChangeStart = fileA.size(); |
133 | if (nextChange != null) |
134 | nextChangeStart = ((nextChange.line1 < nextChange.line0) ? nextChange.line1 : nextChange.line0); |
135 | |
136 | if (nextChangeStart - line0 > contextSize * 2) { // A separate hunk |
137 | nextChangeStart = line0 + contextSize; |
138 | hunkFinish = true; |
139 | } |
140 | if (nextChangeStart > fileA.size()) |
141 | nextChangeStart = fileA.size(); // Limit to file size |
142 | while (line0 < nextChangeStart) { |
143 | hunkLines.add(" " + fileA.get(line0++)); |
144 | line1++; // Keep in sync with trailing context |
145 | } |
146 | |
147 | change = change.link; |
148 | } while (!hunkFinish && change != null); |
149 | |
150 | int hunkStartB = contextStart + cumulExtraLinesBwrtA; |
151 | int hunkTotA = line0 - contextStart; |
152 | int hunkTotB = line1 - hunkStartB; |
153 | |
154 | hunkLines.add(headerPosn, "@@ -" + (contextStart + 1) + ',' + hunkTotA + |
155 | " +" + (hunkStartB + 1) + ',' + hunkTotB + " @@"); |
156 | cumulExtraLinesBwrtA += hunkTotB - hunkTotA; |
157 | |
158 | } while (change != null); |
159 | |
160 | return hunkLines; |
161 | } |
162 | return null; |
163 | } |
164 | |
165 | /* For testing: |
166 | private static void printUniDiff(List<String> fileA, List<String> fileB, int contextSize) { |
167 | List<String> uniDiff = generateUniDiff(fileA, fileB, contextSize); |
168 | |
169 | if (uniDiff != null) |
170 | for (int j = 0; j < uniDiff.size(); j++) |
171 | System.out.println(uniDiff.get(j)); |
172 | } |
173 | */ |
174 | |
175 | |
176 | public static List<BlockDiff> diffLines(List<String> lines, List<String> reference) { |
177 | List<BlockDiff> diffs = new ArrayList<BlockDiff>(); |
178 | |
179 | EGDiff diff = new EGDiff(reference.toArray(), lines.toArray()); |
180 | EGDiff.change change = diff.diff_2(false); |
181 | //printChange(change); |
182 | //printUniDiff(reference, lines, 3); |
183 | |
184 | int l0 = 0, l1 = 0; |
185 | while (change != null) { |
186 | if (change.line0 > l0 && change.line1 > l1) |
187 | diffs.add(new CopyBlock(l0, change.line0-l0)); |
188 | |
189 | if (change.inserted != 0) |
190 | diffs.add(new NewBlock(change.line1, lines.subList(change.line1, change.line1+change.inserted))); |
191 | |
192 | l0 = change.line0 + change.deleted; |
193 | l1 = change.line1 + change.inserted; |
194 | change = change.link; |
195 | } |
196 | |
197 | if (l0 < reference.size()) |
198 | diffs.add(new CopyBlock(l0, reference.size()-l0)); |
199 | |
200 | return diffs; |
201 | } |
202 | |
203 | /** fills files with empty lines to align matching blocks |
204 | * |
205 | * @param file1 first file |
206 | * @param file2 second file |
207 | * @return an array with two lists |
208 | */ |
209 | public static List<ExplodedLine> explode(List<String> file1, List<String> file2) { |
210 | List<ExplodedLine> lines = new ArrayList<ExplodedLine>(); |
211 | List<BlockDiff> diffs = BlockDiffer.diffLines(file2, file1); |
212 | int lastLineCopied = 0, rightOnlyStart = -1, rightPosition = 0; |
213 | for (int i = 0; i < diffs.size(); i++) { |
214 | BlockDiff diff = diffs.get(i); |
215 | if (diff instanceof CopyBlock) { |
216 | CopyBlock copyBlock = (CopyBlock) diff; |
217 | if (lastLineCopied < copyBlock.getFirstLine()) { |
218 | if (rightOnlyStart >= 0) { |
219 | int overlap = Math.min(lines.size()-rightOnlyStart, copyBlock.getFirstLine()-lastLineCopied); |
220 | //lines.subList(rightOnlyStart, rightOnlyStart+overlap).clear(); |
221 | convertRightOnlyToDifferent(lines, rightOnlyStart, overlap, file1, lastLineCopied); |
222 | lastLineCopied += overlap; |
223 | } |
224 | addBlock(lines, LEFT_ONLY, file1, lastLineCopied, copyBlock.getFirstLine(), lastLineCopied, -1); |
225 | } |
226 | addBlock(lines, IDENTICAL, file1, copyBlock.getFirstLine(), copyBlock.getFirstLine()+copyBlock.getLines(), |
227 | copyBlock.getFirstLine(), rightPosition); |
228 | rightPosition += copyBlock.getLines(); |
229 | lastLineCopied = copyBlock.getFirstLine()+copyBlock.getLines(); |
230 | rightOnlyStart = -1; |
231 | } else if (diff instanceof NewBlock) { |
232 | NewBlock newBlock = (NewBlock) diff; |
233 | /*if (nextDiff instanceof BlockDiffer.CopyBlock) { |
234 | BlockDiffer.CopyBlock copyBlock = (BlockDiffer.CopyBlock) nextDiff; |
235 | copyBlock.getFirstLine()-lastLineCopied*/ |
236 | rightOnlyStart = lines.size(); |
237 | addBlock(lines, RIGHT_ONLY, newBlock.getContents(), 0, newBlock.getContents().size(), -1, rightPosition); |
238 | rightPosition += newBlock.getContents().size(); |
239 | } |
240 | } |
241 | |
242 | if (rightOnlyStart >= 0) { |
243 | int overlap = Math.min(lines.size()-rightOnlyStart, file1.size()-lastLineCopied); |
244 | //lines.subList(rightOnlyStart, rightOnlyStart+overlap).clear(); |
245 | convertRightOnlyToDifferent(lines, rightOnlyStart, overlap, file1, lastLineCopied); |
246 | lastLineCopied += overlap; |
247 | } |
248 | addBlock(lines, LEFT_ONLY, file1, lastLineCopied, file1.size(), lastLineCopied, -1); |
249 | |
250 | return lines; |
251 | } |
252 | |
253 | private static void convertRightOnlyToDifferent(List<ExplodedLine> lines, int start, int numLines, |
254 | List<String> leftLines, int leftStart) { |
255 | for (int i = 0; i < numLines; i++) { |
256 | ExplodedLine line = lines.get(start+i); |
257 | lines.set(start+i, |
258 | new ExplodedLine(DIFFERENT, leftLines.get(i+leftStart), line.getRight(), i+leftStart, line.getRightIndex())); |
259 | } |
260 | } |
261 | |
262 | private static void addBlock(List<ExplodedLine> lines, int type, List<String> srcLines, int start, int end, |
263 | int leftStart, int rightStart) { |
264 | for (int i = start; i < end; i++) |
265 | lines.add(new ExplodedLine(type, type == RIGHT_ONLY ? "" : srcLines.get(i), |
266 | type == LEFT_ONLY ? "" : srcLines.get(i), |
267 | type == RIGHT_ONLY ? -1 : i - start + leftStart, |
268 | type == LEFT_ONLY ? -1 : i - start + rightStart)); |
269 | } |
270 | |
271 | public static List<ExplodedLine> condense(List<ExplodedLine> lines) { |
272 | List<ExplodedLine> result = new ArrayList<ExplodedLine>(); |
273 | for (Iterator<ExplodedLine> i = lines.iterator(); i.hasNext();) { |
274 | ExplodedLine line = i.next(); |
275 | if (line.getType() == IDENTICAL) { |
276 | if (result.isEmpty() || result.get(result.size()-1).getType() != IDENTICAL) |
277 | result.add(new ExplodedLine(IDENTICAL, "[...]", "[...]", -1, -1)); |
278 | } else |
279 | result.add(line); |
280 | } |
281 | return result; |
282 | } |
283 | } |
download show line numbers debug dex old transpilations
Travelled to 16 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, ddnzoavkxhuk, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, onxytkatvevr, pyentgdyhuwx, pzhvpgtvlbxg, sawdedvomwva, tslmcundralx, tvejysmllsmz, vouqrxazstgt
No comments. add comment
Snippet ID: | #1000883 |
Snippet name: | class BlockDiffer |
Eternal ID of this version: | #1000883/1 |
Text MD5: | 726e475ca1b8374567b281f5db99c764 |
Author: | stefan |
Category: | javax |
Type: | JavaX fragment (include) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2016-05-25 17:17:34 |
Source code size: | 10251 bytes / 283 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 723 / 6155 |
Referenced in: | [show references] |