Mercurial > hg4j
comparison src/org/tmatesoft/hg/internal/AnnotateFacility.java @ 545:15b406c7cd9d
First round of annotate file is functional
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Fri, 15 Feb 2013 22:15:13 +0100 |
parents | 7f5998a9619d |
children | cd78e8b9d7bc |
comparison
equal
deleted
inserted
replaced
544:7f5998a9619d | 545:15b406c7cd9d |
---|---|
17 package org.tmatesoft.hg.internal; | 17 package org.tmatesoft.hg.internal; |
18 | 18 |
19 import static org.tmatesoft.hg.repo.HgRepository.NO_REVISION; | 19 import static org.tmatesoft.hg.repo.HgRepository.NO_REVISION; |
20 | 20 |
21 import org.tmatesoft.hg.core.Nodeid; | 21 import org.tmatesoft.hg.core.Nodeid; |
22 import org.tmatesoft.hg.internal.PatchGenerator.ChunkSequence; | |
23 import org.tmatesoft.hg.internal.PatchGenerator.LineSequence; | 22 import org.tmatesoft.hg.internal.PatchGenerator.LineSequence; |
24 import org.tmatesoft.hg.repo.HgDataFile; | 23 import org.tmatesoft.hg.repo.HgDataFile; |
25 import org.tmatesoft.hg.repo.HgInvalidStateException; | 24 import org.tmatesoft.hg.repo.HgInvalidStateException; |
26 import org.tmatesoft.hg.repo.HgRepository; | |
27 import org.tmatesoft.hg.util.CancelledException; | 25 import org.tmatesoft.hg.util.CancelledException; |
28 | 26 |
29 /** | 27 /** |
30 * | 28 * |
31 * @author Artem Tikhomirov | 29 * @author Artem Tikhomirov |
35 public class AnnotateFacility { | 33 public class AnnotateFacility { |
36 | 34 |
37 /** | 35 /** |
38 * Annotates changes of the file against its parent(s) | 36 * Annotates changes of the file against its parent(s) |
39 */ | 37 */ |
40 public void annotateChange(HgDataFile df, int changestRevisionIndex, Inspector insp) { | 38 public void annotateChange(HgDataFile df, int changesetRevisionIndex, Inspector insp) { |
41 Nodeid fileRev = df.getRepo().getManifest().getFileRevision(changestRevisionIndex, df.getPath()); | 39 // TODO detect if file is text/binary (e.g. looking for chars < ' ' and not \t\r\n\f |
40 Nodeid fileRev = df.getRepo().getManifest().getFileRevision(changesetRevisionIndex, df.getPath()); | |
42 int fileRevIndex = df.getRevisionIndex(fileRev); | 41 int fileRevIndex = df.getRevisionIndex(fileRev); |
43 int[] fileRevParents = new int[2]; | 42 int[] fileRevParents = new int[2]; |
44 df.parents(fileRevIndex, fileRevParents, null, null); | 43 df.parents(fileRevIndex, fileRevParents, null, null); |
45 if (fileRevParents[0] != NO_REVISION && fileRevParents[1] != NO_REVISION) { | 44 try { |
46 // merge | 45 if (fileRevParents[0] != NO_REVISION && fileRevParents[1] != NO_REVISION) { |
47 } else if (fileRevParents[0] == fileRevParents[1]) { | 46 // merge |
48 // may be equal iff both are unset | 47 } else if (fileRevParents[0] == fileRevParents[1]) { |
49 assert fileRevParents[0] == NO_REVISION; | 48 // may be equal iff both are unset |
50 // everything added | 49 assert fileRevParents[0] == NO_REVISION; |
51 insp.added(null); | 50 // everything added |
52 } else { | 51 ByteArrayChannel c; |
53 int soleParent = fileRevParents[0] == NO_REVISION ? fileRevParents[1] : fileRevParents[0]; | 52 df.content(fileRevIndex, c = new ByteArrayChannel()); |
54 assert soleParent != NO_REVISION; | 53 BlameBlockInspector bbi = new BlameBlockInspector(insp, NO_REVISION, changesetRevisionIndex); |
55 try { | 54 LineSequence cls = LineSequence.newlines(c.toArray()); |
55 bbi.begin(LineSequence.newlines(new byte[0]), cls); | |
56 bbi.match(0, cls.chunkCount()-1, 0); | |
57 bbi.end(); | |
58 } else { | |
59 int soleParent = fileRevParents[0] == NO_REVISION ? fileRevParents[1] : fileRevParents[0]; | |
60 assert soleParent != NO_REVISION; | |
56 ByteArrayChannel c1, c2; | 61 ByteArrayChannel c1, c2; |
57 df.content(soleParent, c1 = new ByteArrayChannel()); | 62 df.content(soleParent, c1 = new ByteArrayChannel()); |
58 df.content(fileRevIndex, c2 = new ByteArrayChannel()); | 63 df.content(fileRevIndex, c2 = new ByteArrayChannel()); |
59 int parentChangesetRevIndex = df.getChangesetRevisionIndex(soleParent); | 64 int parentChangesetRevIndex = df.getChangesetRevisionIndex(soleParent); |
60 PatchGenerator<LineSequence> pg = new PatchGenerator<LineSequence>(); | 65 PatchGenerator<LineSequence> pg = new PatchGenerator<LineSequence>(); |
61 pg.init(LineSequence.newlines(c1.toArray()), LineSequence.newlines(c2.toArray())); | 66 pg.init(LineSequence.newlines(c1.toArray()), LineSequence.newlines(c2.toArray())); |
62 pg.findMatchingBlocks(new BlameBlockInspector(insp)); | 67 pg.findMatchingBlocks(new BlameBlockInspector(insp, parentChangesetRevIndex, changesetRevisionIndex)); |
63 } catch (CancelledException ex) { | 68 } |
64 // TODO likely it was bad idea to throw cancelled exception from content() | 69 } catch (CancelledException ex) { |
65 // deprecate and provide alternative? | 70 // TODO likely it was bad idea to throw cancelled exception from content() |
66 HgInvalidStateException ise = new HgInvalidStateException("ByteArrayChannel never throws CancelledException"); | 71 // deprecate and provide alternative? |
67 ise.initCause(ex); | 72 HgInvalidStateException ise = new HgInvalidStateException("ByteArrayChannel never throws CancelledException"); |
68 throw ise; | 73 ise.initCause(ex); |
69 } | 74 throw ise; |
70 } | 75 } |
71 } | 76 } |
72 | 77 |
73 @Callback | 78 @Callback |
74 public interface Inspector { | 79 public interface Inspector { |
75 void same(Block block); | 80 void same(EqualBlock block); |
76 void added(AddBlock block); | 81 void added(AddBlock block); |
77 void changed(ChangeBlock block); | 82 void changed(ChangeBlock block); |
78 void deleted(DeleteBlock block); | 83 void deleted(DeleteBlock block); |
79 } | 84 } |
80 | 85 |
86 @Callback | |
87 public interface InspectorEx extends Inspector { // XXX better name | |
88 // XXX perhaps, shall pass object instead of separate values for future extension? | |
89 void start(int originLineCount, int targetLineCount); | |
90 void done(); | |
91 } | |
92 | |
81 public interface Block { | 93 public interface Block { |
94 int originChangesetIndex(); | |
95 int targetChangesetIndex(); | |
82 // boolean isMergeRevision(); | 96 // boolean isMergeRevision(); |
83 // int fileRevisionIndex(); | 97 // int fileRevisionIndex(); |
84 // int originFileRevisionIndex(); | 98 // int originFileRevisionIndex(); |
85 // String[] lines(); | 99 // String[] lines(); |
86 // byte[] data(); | 100 // byte[] data(); |
87 } | 101 } |
88 | 102 |
103 public interface EqualBlock extends Block { | |
104 int originStart(); | |
105 int targetStart(); | |
106 int length(); | |
107 } | |
108 | |
89 public interface AddBlock extends Block { | 109 public interface AddBlock extends Block { |
90 int insertedAt(); // line index in the old file | 110 int insertedAt(); // line index in the old file |
91 int firstAddedLine(); | 111 int firstAddedLine(); |
92 int totalAddedLines(); | 112 int totalAddedLines(); |
93 String[] addedLines(); | 113 String[] addedLines(); |
101 public interface ChangeBlock extends AddBlock, DeleteBlock { | 121 public interface ChangeBlock extends AddBlock, DeleteBlock { |
102 } | 122 } |
103 | 123 |
104 static class BlameBlockInspector extends PatchGenerator.DeltaInspector<LineSequence> { | 124 static class BlameBlockInspector extends PatchGenerator.DeltaInspector<LineSequence> { |
105 private final Inspector insp; | 125 private final Inspector insp; |
106 | 126 private final int csetP1; |
107 public BlameBlockInspector(Inspector inspector) { | 127 private final int csetTarget; |
128 | |
129 public BlameBlockInspector(Inspector inspector, int parentCset1, int targetCset) { | |
108 assert inspector != null; | 130 assert inspector != null; |
109 insp = inspector; | 131 insp = inspector; |
132 csetP1 = parentCset1; | |
133 csetTarget = targetCset; | |
134 } | |
135 | |
136 @Override | |
137 public void begin(LineSequence s1, LineSequence s2) { | |
138 super.begin(s1, s2); | |
139 if (insp instanceof InspectorEx) { | |
140 ((InspectorEx) insp).start(s1.chunkCount() - 1, s2.chunkCount() - 1); | |
141 } | |
142 } | |
143 | |
144 @Override | |
145 public void end() { | |
146 super.end(); | |
147 if(insp instanceof InspectorEx) { | |
148 ((InspectorEx) insp).done(); | |
149 } | |
110 } | 150 } |
111 | 151 |
112 @Override | 152 @Override |
113 protected void changed(int s1From, int s1To, int s2From, int s2To) { | 153 protected void changed(int s1From, int s1To, int s2From, int s2To) { |
114 insp.changed(new BlockImpl2(seq1, seq2, s1From, s1To-s1From, s2From, s2To - s2From, s1From, s2From)); | 154 BlockImpl2 block = new BlockImpl2(seq1, seq2, s1From, s1To-s1From, s2From, s2To - s2From, s1From, s2From); |
155 block.setOriginAndTarget(csetP1, csetTarget); | |
156 insp.changed(block); | |
115 } | 157 } |
116 | 158 |
117 @Override | 159 @Override |
118 protected void added(int s1InsertPoint, int s2From, int s2To) { | 160 protected void added(int s1InsertPoint, int s2From, int s2To) { |
119 insp.added(new BlockImpl2(null, seq2, -1, -1, s2From, s2To - s2From, s1InsertPoint, -1)); | 161 BlockImpl2 block = new BlockImpl2(null, seq2, -1, -1, s2From, s2To - s2From, s1InsertPoint, -1); |
162 block.setOriginAndTarget(csetP1, csetTarget); | |
163 insp.added(block); | |
120 } | 164 } |
121 | 165 |
122 @Override | 166 @Override |
123 protected void deleted(int s2DeletePoint, int s1From, int s1To) { | 167 protected void deleted(int s2DeletePoint, int s1From, int s1To) { |
124 insp.deleted(new BlockImpl2(seq1, null, s1From, s1To - s1From, -1, -1, -1, s2DeletePoint)); | 168 BlockImpl2 block = new BlockImpl2(seq1, null, s1From, s1To - s1From, -1, -1, -1, s2DeletePoint); |
169 block.setOriginAndTarget(csetP1, csetTarget); | |
170 insp.deleted(block); | |
125 } | 171 } |
126 | 172 |
127 @Override | 173 @Override |
128 protected void unchanged(int s1From, int s2From, int length) { | 174 protected void unchanged(int s1From, int s2From, int length) { |
129 insp.same(new BlockImpl(seq2, s2From, length)); | 175 BlockImpl1 block = new BlockImpl1(s1From, s2From, length); |
130 } | 176 block.setOriginAndTarget(csetP1, csetTarget); |
131 } | 177 insp.same(block); |
132 | 178 } |
179 } | |
180 | |
133 static class BlockImpl implements Block { | 181 static class BlockImpl implements Block { |
134 private final ChunkSequence seq; | 182 |
135 private final int start; | 183 private int originCset; |
184 private int targetCset; | |
185 | |
186 void setOriginAndTarget(int originChangesetIndex, int targetChangesetIndex) { | |
187 // XXX perhaps, shall be part of Inspector API, rather than Block's | |
188 // as they don't change between blocks (although the moment about merged revisions) | |
189 // is not yet clear to me | |
190 originCset = originChangesetIndex; | |
191 targetCset = targetChangesetIndex; | |
192 } | |
193 | |
194 public int originChangesetIndex() { | |
195 return originCset; | |
196 } | |
197 | |
198 public int targetChangesetIndex() { | |
199 return targetCset; | |
200 } | |
201 } | |
202 | |
203 static class BlockImpl1 extends BlockImpl implements EqualBlock { | |
204 private final int start1, start2; | |
136 private final int length; | 205 private final int length; |
137 | 206 |
138 BlockImpl() { | 207 BlockImpl1(int blockStartSeq1, int blockStartSeq2, int blockLength) { |
139 // FIXME delete this cons | 208 start1 = blockStartSeq1; |
140 seq = null; | 209 start2 = blockStartSeq2; |
141 start = length = -1; | |
142 } | |
143 | |
144 BlockImpl(ChunkSequence s, int blockStart, int blockLength) { | |
145 seq = s; | |
146 start = blockStart; | |
147 length = blockLength; | 210 length = blockLength; |
148 } | 211 } |
149 | 212 |
150 } | 213 public int originStart() { |
151 | 214 return start1; |
152 static class BlockImpl2 implements ChangeBlock { | 215 } |
153 | 216 |
154 private final ChunkSequence oldSeq; | 217 public int targetStart() { |
155 private final ChunkSequence newSeq; | 218 return start2; |
219 } | |
220 | |
221 public int length() { | |
222 return length; | |
223 } | |
224 | |
225 @Override | |
226 public String toString() { | |
227 return String.format("@@ [%d..%d) == [%d..%d) @@", start1, start1+length, start2, start2+length); | |
228 } | |
229 } | |
230 | |
231 static class BlockImpl2 extends BlockImpl implements ChangeBlock { | |
232 | |
233 private final LineSequence oldSeq; | |
234 private final LineSequence newSeq; | |
156 private final int s1Start; | 235 private final int s1Start; |
157 private final int s1Len; | 236 private final int s1Len; |
158 private final int s2Start; | 237 private final int s2Start; |
159 private final int s2Len; | 238 private final int s2Len; |
160 private final int s1InsertPoint; | 239 private final int s1InsertPoint; |
161 private final int s2DeletePoint; | 240 private final int s2DeletePoint; |
162 | 241 |
163 public BlockImpl2(ChunkSequence s1, ChunkSequence s2, int s1Start, int s1Len, int s2Start, int s2Len, int s1InsertPoint, int s2DeletePoint) { | 242 public BlockImpl2(LineSequence s1, LineSequence s2, int s1Start, int s1Len, int s2Start, int s2Len, int s1InsertPoint, int s2DeletePoint) { |
164 oldSeq = s1; | 243 oldSeq = s1; |
165 newSeq = s2; | 244 newSeq = s2; |
166 this.s1Start = s1Start; | 245 this.s1Start = s1Start; |
167 this.s1Len = s1Len; | 246 this.s1Len = s1Len; |
168 this.s2Start = s2Start; | 247 this.s2Start = s2Start; |
208 for (int i = 0; i < count; i++) { | 287 for (int i = 0; i < count; i++) { |
209 rv[i] = String.format("LINE %d", startFrom + i+1); | 288 rv[i] = String.format("LINE %d", startFrom + i+1); |
210 } | 289 } |
211 return rv; | 290 return rv; |
212 } | 291 } |
292 | |
293 @Override | |
294 public String toString() { | |
295 if (s2DeletePoint == -1) { | |
296 return String.format("@@ -%d,0 +%d,%d @@", insertedAt(), firstAddedLine(), totalAddedLines()); | |
297 } else if (s1InsertPoint == -1) { | |
298 // delete only | |
299 return String.format("@@ -%d,%d +%d,0 @@", firstRemovedLine(), totalRemovedLines(), removedAt()); | |
300 } | |
301 return String.format("@@ -%d,%d +%d,%d @@", firstRemovedLine(), totalRemovedLines(), firstAddedLine(), totalAddedLines()); | |
302 } | |
213 } | 303 } |
214 } | 304 } |