Mercurial > hg4j
comparison src/org/tmatesoft/hg/internal/FileAnnotation.java @ 548:ab21ac7dd833
Line-by-line annotation API and support code in place
author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
---|---|
date | Mon, 18 Feb 2013 19:58:51 +0100 |
parents | test/org/tmatesoft/hg/test/FileAnnotation.java@cd78e8b9d7bc |
children | a5fd757d1b5d |
comparison
equal
deleted
inserted
replaced
547:66fc86e8c0dd | 548:ab21ac7dd833 |
---|---|
1 /* | |
2 * Copyright (c) 2013 TMate Software Ltd | |
3 * | |
4 * This program is free software; you can redistribute it and/or modify | |
5 * it under the terms of the GNU General Public License as published by | |
6 * the Free Software Foundation; version 2 of the License. | |
7 * | |
8 * This program is distributed in the hope that it will be useful, | |
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | |
11 * GNU General Public License for more details. | |
12 * | |
13 * For information on how to redistribute this software under | |
14 * the terms of a license other than GNU General Public License | |
15 * contact TMate Software at support@hg4j.com | |
16 */ | |
17 package org.tmatesoft.hg.internal; | |
18 | |
19 import java.util.LinkedList; | |
20 | |
21 import org.tmatesoft.hg.internal.AnnotateFacility.AddBlock; | |
22 import org.tmatesoft.hg.internal.AnnotateFacility.ChangeBlock; | |
23 import org.tmatesoft.hg.internal.AnnotateFacility.DeleteBlock; | |
24 import org.tmatesoft.hg.internal.AnnotateFacility.EqualBlock; | |
25 import org.tmatesoft.hg.internal.AnnotateFacility.LineInspector; | |
26 | |
27 | |
28 /** | |
29 * | |
30 * @author Artem Tikhomirov | |
31 * @author TMate Software Ltd. | |
32 */ | |
33 public class FileAnnotation implements AnnotateFacility.BlockInspectorEx { | |
34 // blocks deleted in the target, as reported at the previous step | |
35 private LinkedList<DeleteBlock> deleted = new LinkedList<DeleteBlock>(); | |
36 // blocks deleted in the origin, to become deletions in target at the next step | |
37 private LinkedList<DeleteBlock> newDeleted = new LinkedList<DeleteBlock>(); | |
38 // keeps <startSeq1, startSeq2, len> of equal blocks, origin to target, from previous step | |
39 // XXX smth like IntSliceVector to access triples (or slices of any size, in fact) | |
40 // with easy indexing, e.g. #get(sliceIndex, indexWithinSlice) | |
41 // and vect.get(7,2) instead of vect.get(7*SIZEOF_SLICE+2) | |
42 private IntVector identical = new IntVector(20*3, 2*3); | |
43 // equal blocks of the current iteration, to be recalculated before next step | |
44 // to track line number (current target to ultimate target) mapping | |
45 private IntVector newIdentical = new IntVector(20*3, 2*3); | |
46 | |
47 private boolean[] knownLines; | |
48 private final LineInspector delegate; | |
49 | |
50 public FileAnnotation(AnnotateFacility.LineInspector lineInspector) { | |
51 delegate = lineInspector; | |
52 } | |
53 | |
54 public void start(int originLineCount, int targetLineCount) { | |
55 if (knownLines == null) { | |
56 knownLines = new boolean[targetLineCount]; | |
57 } | |
58 } | |
59 | |
60 // private static void ppp(IntVector v) { | |
61 // for (int i = 0; i < v.size(); i+= 3) { | |
62 // int len = v.get(i+2); | |
63 // System.out.printf("[%d..%d) == [%d..%d); ", v.get(i), v.get(i) + len, v.get(i+1), v.get(i+1) + len); | |
64 // } | |
65 // System.out.println(); | |
66 // } | |
67 | |
68 public void done() { | |
69 if (identical.size() > 0) { | |
70 // update line numbers of the intermediate target to point to ultimate target's line numbers | |
71 IntVector v = new IntVector(identical.size(), 2*3); | |
72 for (int i = 0; i < newIdentical.size(); i+= 3) { | |
73 int originLine = newIdentical.get(i); | |
74 int targetLine = newIdentical.get(i+1); | |
75 int length = newIdentical.get(i+2); | |
76 int startTargetLine = -1, startOriginLine = -1, c = 0; | |
77 for (int j = 0; j < length; j++) { | |
78 int lnInFinal = mapLineIndex(targetLine + j); | |
79 if (lnInFinal == -1 || (startTargetLine != -1 && lnInFinal != startTargetLine + c)) { | |
80 // the line is not among "same" in ultimate origin | |
81 // or belongs to another/next "same" chunk | |
82 if (startOriginLine == -1) { | |
83 continue; | |
84 } | |
85 v.add(startOriginLine); | |
86 v.add(startTargetLine); | |
87 v.add(c); | |
88 c = 0; | |
89 startOriginLine = startTargetLine = -1; | |
90 // fall-through to check if it's not complete miss but a next chunk | |
91 } | |
92 if (lnInFinal != -1) { | |
93 if (startOriginLine == -1) { | |
94 startOriginLine = originLine + j; | |
95 startTargetLine = lnInFinal; | |
96 c = 1; | |
97 } else { | |
98 assert lnInFinal == startTargetLine + c; | |
99 c++; | |
100 } | |
101 } | |
102 } | |
103 if (startOriginLine != -1) { | |
104 assert c > 0; | |
105 v.add(startOriginLine); | |
106 v.add(startTargetLine); | |
107 v.add(c); | |
108 } | |
109 } | |
110 newIdentical.clear(); | |
111 identical = v; | |
112 } else { | |
113 IntVector li = newIdentical; | |
114 newIdentical = identical; | |
115 identical = li; | |
116 } | |
117 LinkedList<DeleteBlock> ld = newDeleted; | |
118 deleted.clear(); | |
119 newDeleted = deleted; | |
120 deleted = ld; | |
121 } | |
122 | |
123 public void same(EqualBlock block) { | |
124 newIdentical.add(block.originStart()); | |
125 newIdentical.add(block.targetStart()); | |
126 newIdentical.add(block.length()); | |
127 } | |
128 | |
129 public void added(AddBlock block) { | |
130 for (int i = 0, ln = block.firstAddedLine(), x = block.totalAddedLines(); i < x; i++, ln++) { | |
131 int lnInFinal = mapLineIndex(ln); | |
132 if (lnInFinal != -1 && !knownLines[lnInFinal]) { | |
133 delegate.line(lnInFinal, block.targetChangesetIndex(), new LineDescriptor()); | |
134 knownLines[lnInFinal] = true; | |
135 } | |
136 } | |
137 } | |
138 | |
139 public void changed(ChangeBlock block) { | |
140 deleted(block); | |
141 added(block); | |
142 } | |
143 | |
144 public void deleted(DeleteBlock block) { | |
145 newDeleted.add(block); | |
146 } | |
147 | |
148 // line - index in the target | |
149 private boolean isDeleted(int line) { | |
150 for (DeleteBlock b : deleted) { | |
151 if (b.firstRemovedLine() > line) { | |
152 break; | |
153 } | |
154 // line >= b.firstRemovedLine | |
155 if (b.firstRemovedLine() + b.totalRemovedLines() > line) { | |
156 return true; | |
157 } | |
158 } | |
159 return false; | |
160 } | |
161 | |
162 // map target lines to the lines of the revision being annotated (the one that came first) | |
163 private int mapLineIndex(int ln) { | |
164 if (isDeleted(ln)) { | |
165 return -1; | |
166 } | |
167 if (identical.isEmpty()) { | |
168 return ln; | |
169 } | |
170 for (int i = 0; i < identical.size(); i += 3) { | |
171 final int originStart = identical.get(i); | |
172 if (originStart > ln) { | |
173 // assert false; | |
174 return -1; | |
175 } | |
176 // ln >= b.originStart | |
177 final int length = identical.get(i+2); | |
178 if (originStart + length > ln) { | |
179 int targetStart = identical.get(i+1); | |
180 return targetStart + (ln - originStart); | |
181 } | |
182 } | |
183 // assert false; | |
184 return -1; | |
185 } | |
186 | |
187 private final class LineDescriptor implements AnnotateFacility.LineDescriptor { | |
188 LineDescriptor() { | |
189 } | |
190 | |
191 public int totalLines() { | |
192 return FileAnnotation.this.knownLines.length; | |
193 } | |
194 } | |
195 } |