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; |