Mercurial > jhg
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 } |
