comparison src/org/tmatesoft/hg/internal/FileAnnotation.java @ 555:e623aa2ca526

Annotate: RevisionDescriptor provides extra knowledge about inspected/annotated revision
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Fri, 22 Feb 2013 19:03:25 +0100
parents a5fd757d1b5d
children e55f17a7a195
comparison
equal deleted inserted replaced
554:a5fd757d1b5d 555:e623aa2ca526
16 */ 16 */
17 package org.tmatesoft.hg.internal; 17 package org.tmatesoft.hg.internal;
18 18
19 import java.util.LinkedList; 19 import java.util.LinkedList;
20 20
21 import org.tmatesoft.hg.internal.AnnotateFacility.AddBlock; 21 import org.tmatesoft.hg.core.HgIterateDirection;
22 import org.tmatesoft.hg.internal.AnnotateFacility.BlockData; 22 import org.tmatesoft.hg.internal.AnnotateFacility.*;
23 import org.tmatesoft.hg.internal.AnnotateFacility.ChangeBlock; 23 import org.tmatesoft.hg.repo.HgDataFile;
24 import org.tmatesoft.hg.internal.AnnotateFacility.DeleteBlock;
25 import org.tmatesoft.hg.internal.AnnotateFacility.EqualBlock;
26 import org.tmatesoft.hg.internal.AnnotateFacility.LineInspector;
27
28 24
29 /** 25 /**
26 * Produce output like 'hg annotate' does
30 * 27 *
31 * @author Artem Tikhomirov 28 * @author Artem Tikhomirov
32 * @author TMate Software Ltd. 29 * @author TMate Software Ltd.
33 */ 30 */
34 public class FileAnnotation implements AnnotateFacility.BlockInspectorEx { 31 public class FileAnnotation implements AnnotateFacility.BlockInspector, RevisionDescriptor.Recipient {
35 // blocks deleted in the target, as reported at the previous step 32
36 private LinkedList<DeleteBlock> deleted = new LinkedList<DeleteBlock>(); 33 @Experimental(reason="The line-by-line inspector likely to become part of core/command API")
37 // blocks deleted in the origin, to become deletions in target at the next step 34 @Callback
38 private LinkedList<DeleteBlock> newDeleted = new LinkedList<DeleteBlock>(); 35 public interface LineInspector {
39 // keeps <startSeq1, startSeq2, len> of equal blocks, origin to target, from previous step 36 /**
40 // XXX smth like IntSliceVector to access triples (or slices of any size, in fact) 37 * Not necessarily invoked sequentially by line numbers
41 // with easy indexing, e.g. #get(sliceIndex, indexWithinSlice) 38 */
42 // and vect.get(7,2) instead of vect.get(7*SIZEOF_SLICE+2) 39 void line(int lineNumber, int changesetRevIndex, LineDescriptor ld);
43 private IntVector identical = new IntVector(20*3, 2*3); 40 }
44 // equal blocks of the current iteration, to be recalculated before next step 41
45 // to track line number (current target to ultimate target) mapping 42 public interface LineDescriptor {
46 private IntVector newIdentical = new IntVector(20*3, 2*3); 43 int totalLines();
47 44 }
48 private boolean[] knownLines; 45
49 private final LineInspector delegate; 46 /**
50 47 * Annotate file revision, line by line.
51 public FileAnnotation(AnnotateFacility.LineInspector lineInspector) { 48 */
52 delegate = lineInspector; 49 public static void annotate(HgDataFile df, int changelogRevisionIndex, LineInspector insp) {
53 } 50 if (!df.exists()) {
54 51 return;
55 public void start(BlockData originContent, BlockData targetContent) { 52 }
56 if (knownLines == null) { 53 FileAnnotation fa = new FileAnnotation(insp);
57 knownLines = new boolean[targetContent.elementCount()]; 54 AnnotateFacility af = new AnnotateFacility();
58 } 55 af.annotate(df, changelogRevisionIndex, fa, HgIterateDirection.NewToOld);
59 } 56 }
60 57
61 // private static void ppp(IntVector v) { 58 // blocks deleted in the target, as reported at the previous step
62 // for (int i = 0; i < v.size(); i+= 3) { 59 private LinkedList<DeleteBlock> deleted = new LinkedList<DeleteBlock>();
63 // int len = v.get(i+2); 60 // blocks deleted in the origin, to become deletions in target at the next step
64 // System.out.printf("[%d..%d) == [%d..%d); ", v.get(i), v.get(i) + len, v.get(i+1), v.get(i+1) + len); 61 private LinkedList<DeleteBlock> newDeleted = new LinkedList<DeleteBlock>();
65 // } 62 // keeps <startSeq1, startSeq2, len> of equal blocks, origin to target, from previous step
66 // System.out.println(); 63 // XXX smth like IntSliceVector to access triples (or slices of any size, in fact)
67 // } 64 // with easy indexing, e.g. #get(sliceIndex, indexWithinSlice)
68 65 // and vect.get(7,2) instead of vect.get(7*SIZEOF_SLICE+2)
69 public void done() { 66 private IntVector identical = new IntVector(20 * 3, 2 * 3);
70 if (identical.size() > 0) { 67 // equal blocks of the current iteration, to be recalculated before next step
71 // update line numbers of the intermediate target to point to ultimate target's line numbers 68 // to track line number (current target to ultimate target) mapping
72 IntVector v = new IntVector(identical.size(), 2*3); 69 private IntVector newIdentical = new IntVector(20 * 3, 2 * 3);
73 for (int i = 0; i < newIdentical.size(); i+= 3) { 70
74 int originLine = newIdentical.get(i); 71 private boolean[] knownLines;
75 int targetLine = newIdentical.get(i+1); 72 private final LineInspector delegate;
76 int length = newIdentical.get(i+2); 73
77 int startTargetLine = -1, startOriginLine = -1, c = 0; 74 public FileAnnotation(LineInspector lineInspector) {
78 for (int j = 0; j < length; j++) { 75 delegate = lineInspector;
79 int lnInFinal = mapLineIndex(targetLine + j); 76 }
80 if (lnInFinal == -1 || (startTargetLine != -1 && lnInFinal != startTargetLine + c)) { 77
81 // the line is not among "same" in ultimate origin 78 public void start(RevisionDescriptor rd) {
82 // or belongs to another/next "same" chunk 79 if (knownLines == null) {
83 if (startOriginLine == -1) { 80 knownLines = new boolean[rd.target().elementCount()];
84 continue; 81 }
85 } 82 }
86 v.add(startOriginLine); 83
87 v.add(startTargetLine); 84 // private static void ppp(IntVector v) {
88 v.add(c); 85 // for (int i = 0; i < v.size(); i+= 3) {
89 c = 0; 86 // int len = v.get(i+2);
90 startOriginLine = startTargetLine = -1; 87 // System.out.printf("[%d..%d) == [%d..%d); ", v.get(i), v.get(i) + len, v.get(i+1), v.get(i+1) + len);
91 // fall-through to check if it's not complete miss but a next chunk 88 // }
89 // System.out.println();
90 // }
91
92 public void done(RevisionDescriptor rd) {
93 if (identical.size() > 0) {
94 // update line numbers of the intermediate target to point to ultimate target's line numbers
95 IntVector v = new IntVector(identical.size(), 2 * 3);
96 for (int i = 0; i < newIdentical.size(); i += 3) {
97 int originLine = newIdentical.get(i);
98 int targetLine = newIdentical.get(i + 1);
99 int length = newIdentical.get(i + 2);
100 int startTargetLine = -1, startOriginLine = -1, c = 0;
101 for (int j = 0; j < length; j++) {
102 int lnInFinal = mapLineIndex(targetLine + j);
103 if (lnInFinal == -1 || (startTargetLine != -1 && lnInFinal != startTargetLine + c)) {
104 // the line is not among "same" in ultimate origin
105 // or belongs to another/next "same" chunk
106 if (startOriginLine == -1) {
107 continue;
92 } 108 }
93 if (lnInFinal != -1) {
94 if (startOriginLine == -1) {
95 startOriginLine = originLine + j;
96 startTargetLine = lnInFinal;
97 c = 1;
98 } else {
99 assert lnInFinal == startTargetLine + c;
100 c++;
101 }
102 }
103 }
104 if (startOriginLine != -1) {
105 assert c > 0;
106 v.add(startOriginLine); 109 v.add(startOriginLine);
107 v.add(startTargetLine); 110 v.add(startTargetLine);
108 v.add(c); 111 v.add(c);
112 c = 0;
113 startOriginLine = startTargetLine = -1;
114 // fall-through to check if it's not complete miss but a next chunk
115 }
116 if (lnInFinal != -1) {
117 if (startOriginLine == -1) {
118 startOriginLine = originLine + j;
119 startTargetLine = lnInFinal;
120 c = 1;
121 } else {
122 assert lnInFinal == startTargetLine + c;
123 c++;
124 }
109 } 125 }
110 } 126 }
111 newIdentical.clear(); 127 if (startOriginLine != -1) {
112 identical = v; 128 assert c > 0;
113 } else { 129 v.add(startOriginLine);
114 IntVector li = newIdentical; 130 v.add(startTargetLine);
115 newIdentical = identical; 131 v.add(c);
116 identical = li;
117 }
118 LinkedList<DeleteBlock> ld = newDeleted;
119 deleted.clear();
120 newDeleted = deleted;
121 deleted = ld;
122 }
123
124 public void same(EqualBlock block) {
125 newIdentical.add(block.originStart());
126 newIdentical.add(block.targetStart());
127 newIdentical.add(block.length());
128 }
129
130 public void added(AddBlock block) {
131 for (int i = 0, ln = block.firstAddedLine(), x = block.totalAddedLines(); i < x; i++, ln++) {
132 int lnInFinal = mapLineIndex(ln);
133 if (lnInFinal != -1 && !knownLines[lnInFinal]) {
134 delegate.line(lnInFinal, block.targetChangesetIndex(), new LineDescriptor());
135 knownLines[lnInFinal] = true;
136 } 132 }
137 } 133 }
138 } 134 newIdentical.clear();
139 135 identical = v;
140 public void changed(ChangeBlock block) { 136 } else {
141 deleted(block); 137 IntVector li = newIdentical;
142 added(block); 138 newIdentical = identical;
143 } 139 identical = li;
144 140 }
145 public void deleted(DeleteBlock block) { 141 LinkedList<DeleteBlock> ld = newDeleted;
146 newDeleted.add(block); 142 deleted.clear();
147 } 143 newDeleted = deleted;
148 144 deleted = ld;
149 // line - index in the target 145 }
150 private boolean isDeleted(int line) { 146
151 for (DeleteBlock b : deleted) { 147 public void same(EqualBlock block) {
152 if (b.firstRemovedLine() > line) { 148 newIdentical.add(block.originStart());
153 break; 149 newIdentical.add(block.targetStart());
154 } 150 newIdentical.add(block.length());
155 // line >= b.firstRemovedLine 151 }
156 if (b.firstRemovedLine() + b.totalRemovedLines() > line) { 152
157 return true; 153 public void added(AddBlock block) {
158 } 154 for (int i = 0, ln = block.firstAddedLine(), x = block.totalAddedLines(); i < x; i++, ln++) {
159 } 155 int lnInFinal = mapLineIndex(ln);
160 return false; 156 if (lnInFinal != -1 && !knownLines[lnInFinal]) {
161 } 157 delegate.line(lnInFinal, block.targetChangesetIndex(), new LineDescriptorImpl());
162 158 knownLines[lnInFinal] = true;
163 // map target lines to the lines of the revision being annotated (the one that came first) 159 }
164 private int mapLineIndex(int ln) { 160 }
165 if (isDeleted(ln)) { 161 }
162
163 public void changed(ChangeBlock block) {
164 deleted(block);
165 added(block);
166 }
167
168 public void deleted(DeleteBlock block) {
169 newDeleted.add(block);
170 }
171
172 // line - index in the target
173 private boolean isDeleted(int line) {
174 for (DeleteBlock b : deleted) {
175 if (b.firstRemovedLine() > line) {
176 break;
177 }
178 // line >= b.firstRemovedLine
179 if (b.firstRemovedLine() + b.totalRemovedLines() > line) {
180 return true;
181 }
182 }
183 return false;
184 }
185
186 // map target lines to the lines of the revision being annotated (the one that came first)
187 private int mapLineIndex(int ln) {
188 if (isDeleted(ln)) {
189 return -1;
190 }
191 if (identical.isEmpty()) {
192 return ln;
193 }
194 for (int i = 0; i < identical.size(); i += 3) {
195 final int originStart = identical.get(i);
196 if (originStart > ln) {
197 // assert false;
166 return -1; 198 return -1;
167 } 199 }
168 if (identical.isEmpty()) { 200 // ln >= b.originStart
169 return ln; 201 final int length = identical.get(i + 2);
170 } 202 if (originStart + length > ln) {
171 for (int i = 0; i < identical.size(); i += 3) { 203 int targetStart = identical.get(i + 1);
172 final int originStart = identical.get(i); 204 return targetStart + (ln - originStart);
173 if (originStart > ln) { 205 }
174 // assert false; 206 }
175 return -1; 207 // assert false;
176 } 208 return -1;
177 // ln >= b.originStart 209 }
178 final int length = identical.get(i+2); 210
179 if (originStart + length > ln) { 211 private final class LineDescriptorImpl implements LineDescriptor {
180 int targetStart = identical.get(i+1); 212 LineDescriptorImpl() {
181 return targetStart + (ln - originStart); 213 }
182 } 214
183 } 215 public int totalLines() {
184 // assert false; 216 return FileAnnotation.this.knownLines.length;
185 return -1; 217 }
186 } 218 }
187 219 }
188 private final class LineDescriptor implements AnnotateFacility.LineDescriptor {
189 LineDescriptor() {
190 }
191
192 public int totalLines() {
193 return FileAnnotation.this.knownLines.length;
194 }
195 }
196 }