|
15 | 15 | */ |
16 | 16 | package com.github.difflib.unifieddiff; |
17 | 17 |
|
| 18 | +import com.github.difflib.patch.AbstractDelta; |
18 | 19 | import java.io.IOException; |
19 | 20 | import java.io.Writer; |
| 21 | +import java.util.ArrayList; |
| 22 | +import java.util.List; |
| 23 | +import java.util.function.Consumer; |
| 24 | +import java.util.logging.Level; |
| 25 | +import java.util.logging.Logger; |
20 | 26 |
|
21 | 27 | /** |
22 | 28 | * |
23 | 29 | * @author Tobias Warneke (t.warneke@gmx.net) |
24 | 30 | */ |
25 | 31 | public class UnifiedDiffWriter { |
26 | 32 |
|
| 33 | + private static final Logger LOG = Logger.getLogger(UnifiedDiffWriter.class.getName()); |
| 34 | + |
27 | 35 | public static void write(UnifiedDiff diff, Writer writer) throws IOException { |
28 | | - writer.write(diff.getHeader()); |
| 36 | + write(diff, line -> { |
| 37 | + try { |
| 38 | + writer.append(line).append("\n"); |
| 39 | + } catch (IOException ex) { |
| 40 | + LOG.log(Level.SEVERE, null, ex); |
| 41 | + } |
| 42 | + }); |
| 43 | + } |
| 44 | + |
| 45 | + public static void write(UnifiedDiff diff, Consumer<String> writer) throws IOException { |
| 46 | + writer.accept(diff.getHeader()); |
29 | 47 |
|
30 | 48 | for (UnifiedDiffFile file : diff.getFiles()) { |
31 | 49 | writeOrNothing(writer, file.getDiffCommand()); |
32 | 50 | if (file.getIndex() != null) { |
33 | | - writer.write("index " + file.getIndex() + "\n"); |
| 51 | + writer.accept("index " + file.getIndex()); |
34 | 52 | } |
35 | 53 | if (file.getFromFile() != null) { |
36 | | - writer.write("--- " + file.getFromFile() + "\n"); |
| 54 | + writer.accept("--- " + file.getFromFile()); |
37 | 55 | } |
38 | 56 | if (file.getToFile() != null) { |
39 | | - writer.write("+++ " + file.getToFile() + "\n"); |
| 57 | + writer.accept("+++ " + file.getToFile()); |
40 | 58 | } |
41 | 59 |
|
| 60 | + List<AbstractDelta<String>> patchDeltas = new ArrayList<>( |
| 61 | + file.getPatch().getDeltas()); |
| 62 | + |
| 63 | + List<AbstractDelta<String>> deltas = new ArrayList<>(); |
| 64 | + |
| 65 | + int contextSize = 0; |
| 66 | + List<String> originalLines = new ArrayList<>(); |
| 67 | + |
| 68 | + AbstractDelta<String> delta = patchDeltas.get(0); |
| 69 | + deltas.add(delta); // add the first Delta to the current set |
| 70 | + // if there's more than 1 Delta, we may need to output them together |
| 71 | + if (patchDeltas.size() > 1) { |
| 72 | + for (int i = 1; i < patchDeltas.size(); i++) { |
| 73 | + int position = delta.getSource().getPosition(); |
| 74 | + |
| 75 | + // Check if the next Delta is too close to the current |
| 76 | + // position. |
| 77 | + // And if it is, add it to the current set |
| 78 | + AbstractDelta<String> nextDelta = patchDeltas.get(i); |
| 79 | + if ((position + delta.getSource().size() + contextSize) >= (nextDelta |
| 80 | + .getSource().getPosition() - contextSize)) { |
| 81 | + deltas.add(nextDelta); |
| 82 | + } else { |
| 83 | + // if it isn't, output the current set, |
| 84 | + // then create a new set and add the current Delta to |
| 85 | + // it. |
| 86 | + processDeltas(writer, originalLines, deltas, contextSize); |
| 87 | + deltas.clear(); |
| 88 | + deltas.add(nextDelta); |
| 89 | + } |
| 90 | + delta = nextDelta; |
| 91 | + } |
| 92 | + |
| 93 | + } |
| 94 | + // don't forget to process the last set of Deltas |
| 95 | + processDeltas(writer, originalLines, deltas, contextSize); |
| 96 | + |
42 | 97 | } |
43 | 98 | if (diff.getTail() != null) { |
44 | | - writer.write("--\n" + diff.getTail()); |
| 99 | + writer.accept("--"); |
| 100 | + writer.accept(diff.getTail()); |
| 101 | + } |
| 102 | + } |
| 103 | + |
| 104 | + private static void processDeltas(Consumer<String> writer, |
| 105 | + List<String> origLines, |
| 106 | + List<AbstractDelta<String>> deltas, int contextSize) { |
| 107 | + List<String> buffer = new ArrayList<>(); |
| 108 | + int origTotal = 0; // counter for total lines output from Original |
| 109 | + int revTotal = 0; // counter for total lines output from Original |
| 110 | + int line; |
| 111 | + |
| 112 | + AbstractDelta<String> curDelta = deltas.get(0); |
| 113 | + |
| 114 | + // NOTE: +1 to overcome the 0-offset Position |
| 115 | + int origStart = curDelta.getSource().getPosition() + 1 - contextSize; |
| 116 | + if (origStart < 1) { |
| 117 | + origStart = 1; |
| 118 | + } |
| 119 | + |
| 120 | + int revStart = curDelta.getTarget().getPosition() + 1 - contextSize; |
| 121 | + if (revStart < 1) { |
| 122 | + revStart = 1; |
| 123 | + } |
| 124 | + |
| 125 | + // find the start of the wrapper context code |
| 126 | + int contextStart = curDelta.getSource().getPosition() - contextSize; |
| 127 | + if (contextStart < 0) { |
| 128 | + contextStart = 0; // clamp to the start of the file |
| 129 | + } |
| 130 | + |
| 131 | + // output the context before the first Delta |
| 132 | + for (line = contextStart; line < curDelta.getSource().getPosition(); line++) { // |
| 133 | + buffer.add(" " + origLines.get(line)); |
| 134 | + origTotal++; |
| 135 | + revTotal++; |
| 136 | + } |
| 137 | + |
| 138 | + // output the first Delta |
| 139 | + getDeltaText(txt -> buffer.add(txt), curDelta); |
| 140 | + origTotal += curDelta.getSource().getLines().size(); |
| 141 | + revTotal += curDelta.getTarget().getLines().size(); |
| 142 | + |
| 143 | + int deltaIndex = 1; |
| 144 | + while (deltaIndex < deltas.size()) { // for each of the other Deltas |
| 145 | + AbstractDelta<String> nextDelta = deltas.get(deltaIndex); |
| 146 | + int intermediateStart = curDelta.getSource().getPosition() |
| 147 | + + curDelta.getSource().getLines().size(); |
| 148 | + for (line = intermediateStart; line < nextDelta.getSource() |
| 149 | + .getPosition(); line++) { |
| 150 | + // output the code between the last Delta and this one |
| 151 | + buffer.add(" " + origLines.get(line)); |
| 152 | + origTotal++; |
| 153 | + revTotal++; |
| 154 | + } |
| 155 | + getDeltaText(txt -> buffer.add(txt), nextDelta); // output the Delta |
| 156 | + origTotal += nextDelta.getSource().getLines().size(); |
| 157 | + revTotal += nextDelta.getTarget().getLines().size(); |
| 158 | + curDelta = nextDelta; |
| 159 | + deltaIndex++; |
| 160 | + } |
| 161 | + |
| 162 | + // Now output the post-Delta context code, clamping the end of the file |
| 163 | + contextStart = curDelta.getSource().getPosition() |
| 164 | + + curDelta.getSource().getLines().size(); |
| 165 | + for (line = contextStart; (line < (contextStart + contextSize)) |
| 166 | + && (line < origLines.size()); line++) { |
| 167 | + buffer.add(" " + origLines.get(line)); |
| 168 | + origTotal++; |
| 169 | + revTotal++; |
| 170 | + } |
| 171 | + |
| 172 | + // Create and insert the block header, conforming to the Unified Diff |
| 173 | + // standard |
| 174 | + writer.accept("@@ -" + origStart + "," + origTotal + " +" + revStart + "," + revTotal + " @@"); |
| 175 | + buffer.forEach(txt -> { |
| 176 | + writer.accept(txt); |
| 177 | + }); |
| 178 | + } |
| 179 | + |
| 180 | + /** |
| 181 | + * getDeltaText returns the lines to be added to the Unified Diff text from the Delta parameter |
| 182 | + * |
| 183 | + * @param delta - the Delta to output |
| 184 | + * @return list of String lines of code. |
| 185 | + * @author Bill James (tankerbay@gmail.com) |
| 186 | + */ |
| 187 | + private static void getDeltaText(Consumer<String> writer, AbstractDelta<String> delta) { |
| 188 | + for (String line : delta.getSource().getLines()) { |
| 189 | + writer.accept("-" + line); |
| 190 | + } |
| 191 | + for (String line : delta.getTarget().getLines()) { |
| 192 | + writer.accept("+" + line); |
45 | 193 | } |
46 | 194 | } |
47 | 195 |
|
48 | | - private static void writeOrNothing(Writer writer, String str) throws IOException { |
| 196 | + private static void writeOrNothing(Consumer<String> writer, String str) throws IOException { |
49 | 197 | if (str != null) { |
50 | | - writer.append(str).append("\n"); |
| 198 | + writer.accept(str); |
51 | 199 | } |
52 | 200 | } |
53 | 201 | } |
0 commit comments