Mercurial > jhg
comparison src/org/tmatesoft/hg/internal/PatchGenerator.java @ 541:946b13196252
PatchGenerator: refactoring to facilitate use in annotate/blame
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
|---|---|
| date | Wed, 13 Feb 2013 19:42:22 +0100 |
| parents | dd4f6311af52 |
| children | a71a05ec11bc |
comparison
equal
deleted
inserted
replaced
| 540:67d4b0f73984 | 541:946b13196252 |
|---|---|
| 45 private Map<ChunkSequence.ByteChain, IntVector> chunk2UseIndex; | 45 private Map<ChunkSequence.ByteChain, IntVector> chunk2UseIndex; |
| 46 private ChunkSequence seq1, seq2; | 46 private ChunkSequence seq1, seq2; |
| 47 | 47 |
| 48 // get filled by #longestMatch, track start of common sequence in seq1 and seq2, respectively | 48 // get filled by #longestMatch, track start of common sequence in seq1 and seq2, respectively |
| 49 private int matchStartS1, matchStartS2; | 49 private int matchStartS1, matchStartS2; |
| 50 // get filled by #findMatchingBlocks, track start of changed/unknown sequence in seq1 and seq2 | 50 |
| 51 private int changeStartS1, changeStartS2; | 51 private MatchInspector matchInspector; |
| 52 | 52 |
| 53 public void init(byte[] data1, byte[] data2) { | 53 public void init(byte[] data1, byte[] data2) { |
| 54 seq1 = new ChunkSequence(data1); | 54 seq1 = new ChunkSequence(data1); |
| 55 seq1.splitByNewlines(); | 55 seq1.splitByNewlines(); |
| 56 seq2 = new ChunkSequence(data2); | 56 seq2 = new ChunkSequence(data2); |
| 79 // } | 79 // } |
| 80 // System.out.println("}"); | 80 // System.out.println("}"); |
| 81 // } | 81 // } |
| 82 } | 82 } |
| 83 | 83 |
| 84 public void findMatchingBlocks() { | 84 public void findMatchingBlocks(MatchInspector insp) { |
| 85 changeStartS1 = changeStartS2 = 0; | 85 insp.begin(seq1, seq2); |
| 86 matchInspector = insp; | |
| 86 findMatchingBlocks(0, seq1.chunkCount(), 0, seq2.chunkCount()); | 87 findMatchingBlocks(0, seq1.chunkCount(), 0, seq2.chunkCount()); |
| 87 if (changeStartS1 < seq1.chunkCount() || changeStartS2 < seq2.chunkCount()) { | 88 insp.end(); |
| 88 reportDeltaElement(seq1.chunkCount(), seq2.chunkCount()); | |
| 89 } | |
| 90 } | 89 } |
| 91 | 90 |
| 92 /** | 91 /** |
| 93 * implementation based on Python's difflib.py and SequenceMatcher | 92 * implementation based on Python's difflib.py and SequenceMatcher |
| 94 */ | 93 */ |
| 125 chunkIndex2MatchCount = newChunkIndex2MatchCount; | 124 chunkIndex2MatchCount = newChunkIndex2MatchCount; |
| 126 } | 125 } |
| 127 return maxLength; | 126 return maxLength; |
| 128 } | 127 } |
| 129 | 128 |
| 130 public void findMatchingBlocks(int startS1, int endS1, int startS2, int endS2) { | 129 private void findMatchingBlocks(int startS1, int endS1, int startS2, int endS2) { |
| 131 int matchLength = longestMatch(startS1, endS1, startS2, endS2); | 130 int matchLength = longestMatch(startS1, endS1, startS2, endS2); |
| 132 if (matchLength > 0) { | 131 if (matchLength > 0) { |
| 133 final int saveStartS1 = matchStartS1, saveStartS2 = matchStartS2; | 132 final int saveStartS1 = matchStartS1, saveStartS2 = matchStartS2; |
| 134 if (startS1 < matchStartS1 && startS2 < matchStartS2) { | 133 if (startS1 < matchStartS1 && startS2 < matchStartS2) { |
| 135 findMatchingBlocks(startS1, matchStartS1, startS2, matchStartS2); | 134 findMatchingBlocks(startS1, matchStartS1, startS2, matchStartS2); |
| 136 } | 135 } |
| 137 reportDeltaElement(saveStartS1, saveStartS2); | 136 matchInspector.match(saveStartS1, saveStartS2, matchLength); |
| 138 changeStartS1 = saveStartS1 + matchLength; | |
| 139 changeStartS2 = saveStartS2 + matchLength; | |
| 140 // System.out.printf("match: from line #%d and line #%d of length %d\n", saveStartS1, saveStartS2, matchLength); | |
| 141 if (saveStartS1+matchLength < endS1 && saveStartS2+matchLength < endS2) { | 137 if (saveStartS1+matchLength < endS1 && saveStartS2+matchLength < endS2) { |
| 142 findMatchingBlocks(saveStartS1 + matchLength, endS1, saveStartS2 + matchLength, endS2); | 138 findMatchingBlocks(saveStartS1 + matchLength, endS1, saveStartS2 + matchLength, endS2); |
| 143 } | 139 } |
| 144 } | 140 } |
| 145 } | 141 } |
| 146 | 142 |
| 147 private Patch deltaCollector; | 143 interface MatchInspector { |
| 148 | 144 void begin(ChunkSequence s1, ChunkSequence s2); |
| 149 private void reportDeltaElement(int i, int j) { | 145 void match(int startSeq1, int startSeq2, int matchLength); |
| 150 if (changeStartS1 < i) { | 146 void end(); |
| 151 if (changeStartS2 < j) { | 147 } |
| 152 System.out.printf("changed [%d..%d) with [%d..%d)\n", changeStartS1, i, changeStartS2, j); | 148 |
| 149 static class MatchDumpInspector implements MatchInspector { | |
| 150 public void begin(ChunkSequence s1, ChunkSequence s2) { | |
| 151 } | |
| 152 | |
| 153 public void match(int startSeq1, int startSeq2, int matchLength) { | |
| 154 System.out.printf("match: from line #%d and line #%d of length %d\n", startSeq1, startSeq2, matchLength); | |
| 155 } | |
| 156 | |
| 157 public void end() { | |
| 158 } | |
| 159 } | |
| 160 | |
| 161 static class DeltaDumpInspector implements MatchInspector { | |
| 162 protected int changeStartS1, changeStartS2; | |
| 163 protected ChunkSequence seq1, seq2; | |
| 164 | |
| 165 | |
| 166 public void begin(ChunkSequence s1, ChunkSequence s2) { | |
| 167 seq1 = s1; | |
| 168 seq2 = s2; | |
| 169 changeStartS1 = changeStartS2 = 0; | |
| 170 } | |
| 171 | |
| 172 public void match(int startSeq1, int startSeq2, int matchLength) { | |
| 173 reportDeltaElement(startSeq1, startSeq2); | |
| 174 changeStartS1 = startSeq1 + matchLength; | |
| 175 changeStartS2 = startSeq2 + matchLength; | |
| 176 } | |
| 177 | |
| 178 public void end() { | |
| 179 if (changeStartS1 < seq1.chunkCount() || changeStartS2 < seq2.chunkCount()) { | |
| 180 reportDeltaElement(seq1.chunkCount(), seq2.chunkCount()); | |
| 181 } | |
| 182 } | |
| 183 | |
| 184 protected void reportDeltaElement(int matchStartSeq1, int matchStartSeq2) { | |
| 185 if (changeStartS1 < matchStartSeq1) { | |
| 186 if (changeStartS2 < matchStartSeq2) { | |
| 187 System.out.printf("changed [%d..%d) with [%d..%d)\n", changeStartS1, matchStartSeq1, changeStartS2, matchStartSeq2); | |
| 188 } else { | |
| 189 assert changeStartS2 == matchStartSeq2; | |
| 190 System.out.printf("deleted [%d..%d)\n", changeStartS1, matchStartSeq1); | |
| 191 } | |
| 153 } else { | 192 } else { |
| 154 assert changeStartS2 == j; | 193 assert changeStartS1 == matchStartSeq1; |
| 155 System.out.printf("deleted [%d..%d)\n", changeStartS1, i); | 194 if(changeStartS2 < matchStartSeq2) { |
| 156 } | 195 System.out.printf("added [%d..%d)\n", changeStartS2, matchStartSeq2); |
| 157 if (deltaCollector != null) { | 196 } else { |
| 197 assert changeStartS2 == matchStartSeq2; | |
| 198 System.out.printf("adjustent equal blocks %d, %d and %d,%d\n", changeStartS1, matchStartSeq1, changeStartS2, matchStartSeq2); | |
| 199 } | |
| 200 } | |
| 201 } | |
| 202 } | |
| 203 | |
| 204 static class PatchFillInspector extends DeltaDumpInspector { | |
| 205 private final Patch deltaCollector; | |
| 206 | |
| 207 PatchFillInspector(Patch p) { | |
| 208 assert p != null; | |
| 209 deltaCollector = p; | |
| 210 } | |
| 211 | |
| 212 @Override | |
| 213 protected void reportDeltaElement(int matchStartSeq1, int matchStartSeq2) { | |
| 214 if (changeStartS1 < matchStartSeq1) { | |
| 158 int from = seq1.chunk(changeStartS1).getOffset(); | 215 int from = seq1.chunk(changeStartS1).getOffset(); |
| 159 int to = seq1.chunk(i).getOffset(); | 216 int to = seq1.chunk(matchStartSeq1).getOffset(); |
| 160 byte[] data = seq2.data(changeStartS2, j); | 217 byte[] data = seq2.data(changeStartS2, matchStartSeq2); |
| 161 deltaCollector.add(from, to, data); | 218 deltaCollector.add(from, to, data); |
| 162 } | |
| 163 } else { | |
| 164 assert changeStartS1 == i; | |
| 165 if(changeStartS2 < j) { | |
| 166 System.out.printf("added [%d..%d)\n", changeStartS2, j); | |
| 167 } else { | 219 } else { |
| 168 assert changeStartS2 == j; | 220 assert changeStartS1 == matchStartSeq1; |
| 169 System.out.printf("adjustent equal blocks %d, %d and %d,%d\n", changeStartS1, i, changeStartS2, j); | |
| 170 } | |
| 171 if (deltaCollector != null) { | |
| 172 int insPoint = seq1.chunk(changeStartS1).getOffset(); | 221 int insPoint = seq1.chunk(changeStartS1).getOffset(); |
| 173 byte[] data = seq2.data(changeStartS2, j); | 222 byte[] data = seq2.data(changeStartS2, matchStartSeq2); |
| 174 deltaCollector.add(insPoint, insPoint, data); | 223 deltaCollector.add(insPoint, insPoint, data); |
| 175 } | 224 } |
| 176 } | 225 } |
| 177 } | 226 } |
| 227 | |
| 228 | |
| 178 | 229 |
| 179 public static void main(String[] args) throws Exception { | 230 public static void main(String[] args) throws Exception { |
| 180 PatchGenerator pg1 = new PatchGenerator(); | 231 PatchGenerator pg1 = new PatchGenerator(); |
| 181 pg1.init("hello".getBytes(), "hello\nworld".getBytes()); | 232 pg1.init("hello".getBytes(), "hello\nworld".getBytes()); |
| 182 pg1.findMatchingBlocks(); | 233 pg1.findMatchingBlocks(new MatchDumpInspector()); |
| 183 if (Boolean.TRUE.booleanValue()) { | 234 pg1.findMatchingBlocks(new DeltaDumpInspector()); |
| 235 if (Boolean.FALSE.booleanValue()) { | |
| 184 return; | 236 return; |
| 185 } | 237 } |
| 186 HgRepository repo = new HgLookup().detectFromWorkingDir(); | 238 HgRepository repo = new HgLookup().detectFromWorkingDir(); |
| 187 HgDataFile df = repo.getFileNode("cmdline/org/tmatesoft/hg/console/Main.java"); | 239 HgDataFile df = repo.getFileNode("cmdline/org/tmatesoft/hg/console/Main.java"); |
| 188 ByteArrayChannel bac1, bac2; | 240 ByteArrayChannel bac1, bac2; |
| 190 df.content(81, bac2 = new ByteArrayChannel()); | 242 df.content(81, bac2 = new ByteArrayChannel()); |
| 191 // String s1 = "line 1\nline 2\r\nline 3\n\nline 1\nline 2"; | 243 // String s1 = "line 1\nline 2\r\nline 3\n\nline 1\nline 2"; |
| 192 // String s2 = "abc\ncdef\r\nline 2\r\nline 3\nline 2"; | 244 // String s2 = "abc\ncdef\r\nline 2\r\nline 3\nline 2"; |
| 193 PatchGenerator pg = new PatchGenerator(); | 245 PatchGenerator pg = new PatchGenerator(); |
| 194 pg.init(bac1.toArray(), bac2.toArray()); | 246 pg.init(bac1.toArray(), bac2.toArray()); |
| 195 pg.findMatchingBlocks(); | 247 System.out.println("Matches:"); |
| 248 pg.findMatchingBlocks(new MatchDumpInspector()); | |
| 249 System.out.println("Deltas:"); | |
| 250 pg.findMatchingBlocks(new DeltaDumpInspector()); | |
| 196 } | 251 } |
| 197 | 252 |
| 198 public Patch delta(byte[] prev, byte[] content) { | 253 public Patch delta(byte[] prev, byte[] content) { |
| 199 deltaCollector = new Patch(); | 254 Patch rv = new Patch(); |
| 200 init(prev, content); | 255 init(prev, content); |
| 201 findMatchingBlocks(); | 256 findMatchingBlocks(new PatchFillInspector(rv)); |
| 202 return deltaCollector; | 257 return rv; |
| 203 } | 258 } |
| 204 | 259 |
| 205 private static class ChunkSequence { | 260 private static class ChunkSequence { |
| 206 | 261 |
| 207 private final byte[] input; | 262 private final byte[] input; |
