comparison src/org/tmatesoft/hg/internal/AnnotateFacility.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
comparison
equal deleted inserted replaced
554:a5fd757d1b5d 555:e623aa2ca526
23 import java.util.LinkedList; 23 import java.util.LinkedList;
24 import java.util.ListIterator; 24 import java.util.ListIterator;
25 25
26 import org.tmatesoft.hg.core.HgIterateDirection; 26 import org.tmatesoft.hg.core.HgIterateDirection;
27 import org.tmatesoft.hg.core.Nodeid; 27 import org.tmatesoft.hg.core.Nodeid;
28 import org.tmatesoft.hg.internal.AnnotateFacility.BlockData; 28 import org.tmatesoft.hg.internal.AnnotateFacility.RevisionDescriptor.Recipient;
29 import org.tmatesoft.hg.internal.DiffHelper.LineSequence; 29 import org.tmatesoft.hg.internal.DiffHelper.LineSequence;
30 import org.tmatesoft.hg.internal.DiffHelper.LineSequence.ByteChain; 30 import org.tmatesoft.hg.internal.DiffHelper.LineSequence.ByteChain;
31 import org.tmatesoft.hg.repo.HgDataFile; 31 import org.tmatesoft.hg.repo.HgDataFile;
32 import org.tmatesoft.hg.repo.HgInvalidStateException; 32 import org.tmatesoft.hg.repo.HgInvalidStateException;
33 import org.tmatesoft.hg.repo.HgRepository;
34 import org.tmatesoft.hg.util.Adaptable;
33 import org.tmatesoft.hg.util.CancelledException; 35 import org.tmatesoft.hg.util.CancelledException;
34 import org.tmatesoft.hg.util.Pair; 36 import org.tmatesoft.hg.util.Pair;
35 37
36 /** 38 /**
39 * Facility with diff/annotate functionality.
37 * 40 *
38 * @author Artem Tikhomirov 41 * @author Artem Tikhomirov
39 * @author TMate Software Ltd. 42 * @author TMate Software Ltd.
40 */ 43 */
41 @Experimental(reason="work in progress") 44 @Experimental(reason="work in progress")
53 DiffHelper<LineSequence> pg = new DiffHelper<LineSequence>(); 56 DiffHelper<LineSequence> pg = new DiffHelper<LineSequence>();
54 pg.init(c1, c2); 57 pg.init(c1, c2);
55 pg.findMatchingBlocks(new BlameBlockInspector(insp, clogRevIndex1, clogRevIndex2)); 58 pg.findMatchingBlocks(new BlameBlockInspector(insp, clogRevIndex1, clogRevIndex2));
56 } 59 }
57 60
61 /**
62 * Walk file history up to revision at given changeset and report changes for each revision
63 */
58 public void annotate(HgDataFile df, int changelogRevisionIndex, BlockInspector insp, HgIterateDirection iterateOrder) { 64 public void annotate(HgDataFile df, int changelogRevisionIndex, BlockInspector insp, HgIterateDirection iterateOrder) {
59 if (!df.exists()) { 65 if (!df.exists()) {
60 return; 66 return;
61 } 67 }
62 // Note, changelogRevisionIndex may be TIP, while #implAnnotateChange doesn't tolerate constants 68 // Note, changelogRevisionIndex may be TIP, while #implAnnotateChange doesn't tolerate constants
105 implAnnotateChange(fileInfoCache, clogRevIndex, fri, fileRevParents, insp); 111 implAnnotateChange(fileInfoCache, clogRevIndex, fri, fileRevParents, insp);
106 } 112 }
107 } 113 }
108 114
109 /** 115 /**
110 * Annotate file revision, line by line. 116 * Annotates changes of the file against its parent(s).
117 * Unlike {@link #annotate(HgDataFile, int, BlockInspector, HgIterateDirection)}, doesn't
118 * walk file history, looks at the specified revision only. Handles both parents (if merge revision).
111 */ 119 */
112 public void annotate(HgDataFile df, int changelogRevisionIndex, LineInspector insp) { 120 public void annotateSingleRevision(HgDataFile df, int changelogRevisionIndex, BlockInspector insp) {
113 if (!df.exists()) {
114 return;
115 }
116 FileAnnotation fa = new FileAnnotation(insp);
117 annotate(df, changelogRevisionIndex, fa, HgIterateDirection.NewToOld);
118 }
119
120 /**
121 * Annotates changes of the file against its parent(s)
122 */
123 public void annotateChange(HgDataFile df, int changelogRevisionIndex, BlockInspector insp) {
124 // TODO detect if file is text/binary (e.g. looking for chars < ' ' and not \t\r\n\f 121 // TODO detect if file is text/binary (e.g. looking for chars < ' ' and not \t\r\n\f
125 int fileRevIndex = fileRevIndex(df, changelogRevisionIndex); 122 int fileRevIndex = fileRevIndex(df, changelogRevisionIndex);
126 int[] fileRevParents = new int[2]; 123 int[] fileRevParents = new int[2];
127 df.parents(fileRevIndex, fileRevParents, null, null); 124 df.parents(fileRevIndex, fileRevParents, null, null);
128 if (changelogRevisionIndex == TIP) { 125 if (changelogRevisionIndex == TIP) {
231 } 228 }
232 return rv; 229 return rv;
233 } 230 }
234 } 231 }
235 232
233 /**
234 * Client's sink for revision differences.
235 *
236 * When implemented, clients shall not expect new {@link Block blocks} instances in each call.
237 *
238 * In case more information about annotated revision is needed, inspector instances may supply
239 * {@link RevisionDescriptor.Recipient} through {@link Adaptable}.
240 */
236 @Callback 241 @Callback
237 public interface BlockInspector { 242 public interface BlockInspector {
238 void same(EqualBlock block); 243 void same(EqualBlock block);
239 void added(AddBlock block); 244 void added(AddBlock block);
240 void changed(ChangeBlock block); 245 void changed(ChangeBlock block);
241 void deleted(DeleteBlock block); 246 void deleted(DeleteBlock block);
242 }
243
244 @Callback
245 public interface BlockInspectorEx extends BlockInspector { // XXX better name
246 // XXX perhaps, shall pass object instead of separate values for future extension?
247 void start(BlockData originContent, BlockData targetContent);
248 void done();
249 } 247 }
250 248
251 /** 249 /**
252 * Represents content of a block, either as a sequence of bytes or a 250 * Represents content of a block, either as a sequence of bytes or a
253 * sequence of smaller blocks (lines), if appropriate (according to usage context). 251 * sequence of smaller blocks (lines), if appropriate (according to usage context).
274 BlockData elementAt(int index); 272 BlockData elementAt(int index);
275 int elementCount(); 273 int elementCount();
276 byte[] asArray(); 274 byte[] asArray();
277 } 275 }
278 276
277 /**
278 * {@link BlockInspector} may optionally request extra information about revisions
279 * being inspected, denoting itself as a {@link RevisionDescriptor.Recipient}. This class
280 * provides complete information about file revision under annotation now.
281 */
282 public interface RevisionDescriptor {
283 /**
284 * @return complete source of the diff origin, never <code>null</code>
285 */
286 BlockData origin();
287 /**
288 * @return complete source of the diff target, never <code>null</code>
289 */
290 BlockData target();
291 /**
292 * @return changeset revision index of original file, or {@link HgRepository#NO_REVISION} if it's the very first revision
293 */
294 int originChangesetIndex();
295 /**
296 * @return changeset revision index of the target file
297 */
298 int targetChangesetIndex();
299 /**
300 * @return <code>true</code> if this revision is merge
301 */
302 boolean isMerge();
303 /**
304 * @return changeset revision index of the second, merged parent
305 */
306 int mergeChangesetIndex();
307 /**
308 * @return revision index of the change in file's revlog
309 */
310 int fileRevisionIndex();
311
312 /**
313 * Implement to indicate interest in {@link RevisionDescriptor}.
314 *
315 * Note, instance of {@link RevisionDescriptor} is the same for
316 * {@link #start(RevisionDescriptor)} and {@link #done(RevisionDescriptor)}
317 * methods, and not necessarily a new one (i.e. <code>==</code>) for the next
318 * revision announced.
319 */
320 @Callback
321 public interface Recipient {
322 /**
323 * Comes prior to any change {@link Block blocks}
324 */
325 void start(RevisionDescriptor revisionDescription);
326 /**
327 * Comes after all change {@link Block blocks} were dispatched
328 */
329 void done(RevisionDescriptor revisionDescription);
330 }
331 }
332
279 public interface Block { 333 public interface Block {
280 int originChangesetIndex(); 334 int originChangesetIndex();
281 int targetChangesetIndex(); 335 int targetChangesetIndex();
282 // boolean isMergeRevision();
283 // int fileRevisionIndex();
284 // int originFileRevisionIndex();
285 // String[] lines();
286 // byte[] data();
287 } 336 }
288 337
289 public interface EqualBlock extends Block { 338 public interface EqualBlock extends Block {
290 int originStart(); 339 int originStart();
291 int targetStart(); 340 int targetStart();
306 BlockData removedLines(); 355 BlockData removedLines();
307 } 356 }
308 public interface ChangeBlock extends AddBlock, DeleteBlock { 357 public interface ChangeBlock extends AddBlock, DeleteBlock {
309 } 358 }
310 359
311 @Callback 360 private static class BlameBlockInspector extends DiffHelper.DeltaInspector<LineSequence> {
312 public interface LineInspector {
313 /**
314 * Not necessarily invoked sequentially by line numbers
315 */
316 void line(int lineNumber, int changesetRevIndex, LineDescriptor ld);
317 }
318
319 public interface LineDescriptor {
320 int totalLines();
321 }
322
323
324
325 static class BlameBlockInspector extends DiffHelper.DeltaInspector<LineSequence> {
326 private final BlockInspector insp; 361 private final BlockInspector insp;
327 private final int csetOrigin; 362 private final int csetOrigin;
328 private final int csetTarget; 363 private final int csetTarget;
329 private EqualBlocksCollector p2MergeCommon; 364 private EqualBlocksCollector p2MergeCommon;
330 private int csetMergeParent; 365 private int csetMergeParent;
331 private IntVector mergeRanges; 366 private IntVector mergeRanges;
332 private ContentBlock originContent, targetContent; 367 private final AnnotateRev annotatedRevision;
333 368
334 public BlameBlockInspector(BlockInspector inspector, int originCset, int targetCset) { 369 public BlameBlockInspector(BlockInspector inspector, int originCset, int targetCset) {
335 assert inspector != null; 370 assert inspector != null;
336 insp = inspector; 371 insp = inspector;
372 annotatedRevision = new AnnotateRev();
337 csetOrigin = originCset; 373 csetOrigin = originCset;
338 csetTarget = targetCset; 374 csetTarget = targetCset;
339 } 375 }
340 376
341 public void setMergeParent2(EqualBlocksCollector p2Merge, int parentCset2) { 377 public void setMergeParent2(EqualBlocksCollector p2Merge, int parentCset2) {
345 } 381 }
346 382
347 @Override 383 @Override
348 public void begin(LineSequence s1, LineSequence s2) { 384 public void begin(LineSequence s1, LineSequence s2) {
349 super.begin(s1, s2); 385 super.begin(s1, s2);
350 originContent = new ContentBlock(s1); 386 ContentBlock originContent = new ContentBlock(s1);
351 targetContent = new ContentBlock(s2); 387 ContentBlock targetContent = new ContentBlock(s2);
352 if (insp instanceof BlockInspectorEx) { 388 annotatedRevision.set(originContent, targetContent);
353 ((BlockInspectorEx) insp).start(originContent, targetContent); 389 annotatedRevision.set(csetOrigin, csetTarget, p2MergeCommon != null ? csetMergeParent : NO_REVISION);
390 Recipient curious = Adaptable.Factory.getAdapter(insp, Recipient.class, null);
391 if (curious != null) {
392 curious.start(annotatedRevision);
354 } 393 }
355 } 394 }
356 395
357 @Override 396 @Override
358 public void end() { 397 public void end() {
359 super.end(); 398 super.end();
360 if(insp instanceof BlockInspectorEx) { 399 Recipient curious = Adaptable.Factory.getAdapter(insp, Recipient.class, null);
361 ((BlockInspectorEx) insp).done(); 400 if (curious != null) {
362 } 401 curious.done(annotatedRevision);
402 }
403 p2MergeCommon = null;
363 } 404 }
364 405
365 @Override 406 @Override
366 protected void changed(int s1From, int s1To, int s2From, int s2To) { 407 protected void changed(int s1From, int s1To, int s2From, int s2To) {
367 if (p2MergeCommon != null) { 408 if (p2MergeCommon != null) {
385 final boolean lastRange = i+3 >= mergeRanges.size(); 426 final boolean lastRange = i+3 >= mergeRanges.size();
386 final int s1LinesLeft = s1TotalLines - s1ConsumedLines; 427 final int s1LinesLeft = s1TotalLines - s1ConsumedLines;
387 // how many lines we may reported as changed (don't use more than in range unless it's the very last range) 428 // how many lines we may reported as changed (don't use more than in range unless it's the very last range)
388 final int s1LinesToBorrow = lastRange ? s1LinesLeft : Math.min(s1LinesLeft, rangeLen); 429 final int s1LinesToBorrow = lastRange ? s1LinesLeft : Math.min(s1LinesLeft, rangeLen);
389 if (s1LinesToBorrow > 0) { 430 if (s1LinesToBorrow > 0) {
390 ChangeBlockImpl block = new ChangeBlockImpl(originContent, targetContent, s1Start, s1LinesToBorrow, rangeStart, rangeLen, s1Start, rangeStart); 431 ChangeBlockImpl block = getChangeBlock(s1Start, s1LinesToBorrow, rangeStart, rangeLen);
391 block.setOriginAndTarget(rangeOrigin, csetTarget); 432 block.setOriginAndTarget(rangeOrigin, csetTarget);
392 insp.changed(block); 433 insp.changed(block);
393 s1ConsumedLines += s1LinesToBorrow; 434 s1ConsumedLines += s1LinesToBorrow;
394 s1Start += s1LinesToBorrow; 435 s1Start += s1LinesToBorrow;
395 } else { 436 } else {
400 } 441 }
401 if (s1ConsumedLines != s1TotalLines) { 442 if (s1ConsumedLines != s1TotalLines) {
402 throw new HgInvalidStateException(String.format("Expected to process %d lines, but actually was %d", s1TotalLines, s1ConsumedLines)); 443 throw new HgInvalidStateException(String.format("Expected to process %d lines, but actually was %d", s1TotalLines, s1ConsumedLines));
403 } 444 }
404 } else { 445 } else {
405 ChangeBlockImpl block = new ChangeBlockImpl(originContent, targetContent, s1From, s1To-s1From, s2From, s2To - s2From, s1From, s2From); 446 ChangeBlockImpl block = getChangeBlock(s1From, s1To-s1From, s2From, s2To - s2From);
406 block.setOriginAndTarget(csetOrigin, csetTarget); 447 block.setOriginAndTarget(csetOrigin, csetTarget);
407 insp.changed(block); 448 insp.changed(block);
408 } 449 }
409 } 450 }
410 451
431 } 472 }
432 } 473 }
433 474
434 @Override 475 @Override
435 protected void deleted(int s2DeletePoint, int s1From, int s1To) { 476 protected void deleted(int s2DeletePoint, int s1From, int s1To) {
436 ChangeBlockImpl block = new ChangeBlockImpl(originContent, null, s1From, s1To - s1From, -1, -1, -1, s2DeletePoint); 477 ChangeBlockImpl block = new ChangeBlockImpl(annotatedRevision.origin, null, s1From, s1To - s1From, -1, -1, -1, s2DeletePoint);
437 block.setOriginAndTarget(csetOrigin, csetTarget); 478 block.setOriginAndTarget(csetOrigin, csetTarget);
438 insp.deleted(block); 479 insp.deleted(block);
439 } 480 }
440 481
441 @Override 482 @Override
442 protected void unchanged(int s1From, int s2From, int length) { 483 protected void unchanged(int s1From, int s2From, int length) {
443 EqualBlockImpl block = new EqualBlockImpl(s1From, s2From, length, targetContent); 484 EqualBlockImpl block = new EqualBlockImpl(s1From, s2From, length, annotatedRevision.target);
444 block.setOriginAndTarget(csetOrigin, csetTarget); 485 block.setOriginAndTarget(csetOrigin, csetTarget);
445 insp.same(block); 486 insp.same(block);
446 } 487 }
447 488
448 private ChangeBlockImpl getAddBlock(int start, int len, int insPoint) { 489 private ChangeBlockImpl getAddBlock(int start, int len, int insPoint) {
449 return new ChangeBlockImpl(null, targetContent, -1, -1, start, len, insPoint, -1); 490 return new ChangeBlockImpl(null, annotatedRevision.target, -1, -1, start, len, insPoint, -1);
450 } 491 }
451 } 492
452 493 private ChangeBlockImpl getChangeBlock(int start1, int end1, int start2, int end2) {
453 static class BlockImpl implements Block { 494 return new ChangeBlockImpl(annotatedRevision.origin, annotatedRevision.target, start1, end1-start1, start2, end2-start2, start1, start2);
495 }
496 }
497
498 private static class BlockImpl implements Block {
454 499
455 private int originCset; 500 private int originCset;
456 private int targetCset; 501 private int targetCset;
457 502
458 void setOriginAndTarget(int originChangesetIndex, int targetChangesetIndex) { 503 void setOriginAndTarget(int originChangesetIndex, int targetChangesetIndex) {
470 public int targetChangesetIndex() { 515 public int targetChangesetIndex() {
471 return targetCset; 516 return targetCset;
472 } 517 }
473 } 518 }
474 519
475 static class EqualBlockImpl extends BlockImpl implements EqualBlock { 520 private static class EqualBlockImpl extends BlockImpl implements EqualBlock {
476 private final int start1, start2; 521 private final int start1, start2;
477 private final int length; 522 private final int length;
478 private final ContentBlock fullContent; 523 private final ContentBlock fullContent;
479 private FilterBlock myContent; 524 private FilterBlock myContent;
480 525
508 public String toString() { 553 public String toString() {
509 return String.format("@@ [%d..%d) == [%d..%d) @@", start1, start1+length, start2, start2+length); 554 return String.format("@@ [%d..%d) == [%d..%d) @@", start1, start1+length, start2, start2+length);
510 } 555 }
511 } 556 }
512 557
513 static class ChangeBlockImpl extends BlockImpl implements ChangeBlock { 558 private static class ChangeBlockImpl extends BlockImpl implements ChangeBlock {
514 559
515 private final ContentBlock oldContent; 560 private final ContentBlock oldContent;
516 private final ContentBlock newContent; 561 private final ContentBlock newContent;
517 private final int s1Start; 562 private final int s1Start;
518 private final int s1Len; 563 private final int s1Len;
756 } 801 }
757 return false; 802 return false;
758 } 803 }
759 } 804 }
760 805
806 private static class AnnotateRev implements RevisionDescriptor {
807 public ContentBlock origin, target;
808 public int originCset, targetCset, mergeCset, fileRevIndex;
809
810 public void set(ContentBlock o, ContentBlock t) {
811 origin = o;
812 target = t;
813 }
814 public void set(int o, int t, int m) {
815 originCset = o;
816 targetCset = t;
817 mergeCset = m;
818 }
819
820 public BlockData origin() {
821 return origin;
822 }
823
824 public BlockData target() {
825 return target;
826 }
827
828 public int originChangesetIndex() {
829 return originCset;
830 }
831
832 public int targetChangesetIndex() {
833 return targetCset;
834 }
835
836 public boolean isMerge() {
837 return mergeCset != NO_REVISION;
838 }
839
840 public int mergeChangesetIndex() {
841 return mergeCset;
842 }
843
844 public int fileRevisionIndex() {
845 return fileRevIndex;
846 }
847 }
848
761 public static void main(String[] args) { 849 public static void main(String[] args) {
762 EqualBlocksCollector bc = new EqualBlocksCollector(); 850 EqualBlocksCollector bc = new EqualBlocksCollector();
763 bc.match(-1, 5, 3); 851 bc.match(-1, 5, 3);
764 bc.match(-1, 10, 2); 852 bc.match(-1, 10, 2);
765 bc.match(-1, 15, 3); 853 bc.match(-1, 15, 3);