comparison src/org/tmatesoft/hg/internal/AnnotateFacility.java @ 554:a5fd757d1b5d

Access to content of annotated files through BlockData interface
author Artem Tikhomirov <tikhomirov.artem@gmail.com>
date Thu, 21 Feb 2013 21:53:55 +0100
parents 093a2022dad5
children e623aa2ca526
comparison
equal deleted inserted replaced
553:093a2022dad5 554:a5fd757d1b5d
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.DiffHelper.LineSequence; 29 import org.tmatesoft.hg.internal.DiffHelper.LineSequence;
30 import org.tmatesoft.hg.internal.DiffHelper.LineSequence.ByteChain;
29 import org.tmatesoft.hg.repo.HgDataFile; 31 import org.tmatesoft.hg.repo.HgDataFile;
30 import org.tmatesoft.hg.repo.HgInvalidStateException; 32 import org.tmatesoft.hg.repo.HgInvalidStateException;
31 import org.tmatesoft.hg.util.CancelledException; 33 import org.tmatesoft.hg.util.CancelledException;
32 import org.tmatesoft.hg.util.Pair; 34 import org.tmatesoft.hg.util.Pair;
33 35
240 } 242 }
241 243
242 @Callback 244 @Callback
243 public interface BlockInspectorEx extends BlockInspector { // XXX better name 245 public interface BlockInspectorEx extends BlockInspector { // XXX better name
244 // XXX perhaps, shall pass object instead of separate values for future extension? 246 // XXX perhaps, shall pass object instead of separate values for future extension?
245 void start(int originLineCount, int targetLineCount); 247 void start(BlockData originContent, BlockData targetContent);
246 void done(); 248 void done();
249 }
250
251 /**
252 * 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).
254 *
255 * This approach allows line-by-line access to content data along with complete byte sequence for the whole block, i.e.
256 * <pre>
257 * BlockData bd = addBlock.addedLines()
258 * // bd describes data from the addition completely.
259 * // elements of the BlockData are lines
260 * bd.elementCount() == addBlock.totalAddedLines();
261 * // one cat obtain complete addition with
262 * byte[] everythingAdded = bd.asArray();
263 * // or iterate line by line
264 * for (int i = 0; i < bd.elementCount(); i++) {
265 * byte[] lineContent = bd.elementAt(i);
266 * String line = new String(lineContent, fileEncodingCharset);
267 * }
268 * where bd.elementAt(0) is the line at index addBlock.firstAddedLine()
269 * </pre>
270 *
271 * LineData or ChunkData?
272 */
273 public interface BlockData {
274 BlockData elementAt(int index);
275 int elementCount();
276 byte[] asArray();
247 } 277 }
248 278
249 public interface Block { 279 public interface Block {
250 int originChangesetIndex(); 280 int originChangesetIndex();
251 int targetChangesetIndex(); 281 int targetChangesetIndex();
258 288
259 public interface EqualBlock extends Block { 289 public interface EqualBlock extends Block {
260 int originStart(); 290 int originStart();
261 int targetStart(); 291 int targetStart();
262 int length(); 292 int length();
293 BlockData content();
263 } 294 }
264 295
265 public interface AddBlock extends Block { 296 public interface AddBlock extends Block {
266 int insertedAt(); // line index in the old file 297 int insertedAt(); // line index in the old file
267 int firstAddedLine(); 298 int firstAddedLine();
268 int totalAddedLines(); 299 int totalAddedLines();
269 String[] addedLines(); 300 BlockData addedLines();
270 } 301 }
271 public interface DeleteBlock extends Block { 302 public interface DeleteBlock extends Block {
272 int removedAt(); // line index in the new file 303 int removedAt(); // line index in the new file
273 int firstRemovedLine(); 304 int firstRemovedLine();
274 int totalRemovedLines(); 305 int totalRemovedLines();
275 String[] removedLines(); 306 BlockData removedLines();
276 } 307 }
277 public interface ChangeBlock extends AddBlock, DeleteBlock { 308 public interface ChangeBlock extends AddBlock, DeleteBlock {
278 } 309 }
279 310
280 @Callback 311 @Callback
296 private final int csetOrigin; 327 private final int csetOrigin;
297 private final int csetTarget; 328 private final int csetTarget;
298 private EqualBlocksCollector p2MergeCommon; 329 private EqualBlocksCollector p2MergeCommon;
299 private int csetMergeParent; 330 private int csetMergeParent;
300 private IntVector mergeRanges; 331 private IntVector mergeRanges;
332 private ContentBlock originContent, targetContent;
301 333
302 public BlameBlockInspector(BlockInspector inspector, int originCset, int targetCset) { 334 public BlameBlockInspector(BlockInspector inspector, int originCset, int targetCset) {
303 assert inspector != null; 335 assert inspector != null;
304 insp = inspector; 336 insp = inspector;
305 csetOrigin = originCset; 337 csetOrigin = originCset;
313 } 345 }
314 346
315 @Override 347 @Override
316 public void begin(LineSequence s1, LineSequence s2) { 348 public void begin(LineSequence s1, LineSequence s2) {
317 super.begin(s1, s2); 349 super.begin(s1, s2);
350 originContent = new ContentBlock(s1);
351 targetContent = new ContentBlock(s2);
318 if (insp instanceof BlockInspectorEx) { 352 if (insp instanceof BlockInspectorEx) {
319 ((BlockInspectorEx) insp).start(s1.chunkCount() - 1, s2.chunkCount() - 1); 353 ((BlockInspectorEx) insp).start(originContent, targetContent);
320 } 354 }
321 } 355 }
322 356
323 @Override 357 @Override
324 public void end() { 358 public void end() {
351 final boolean lastRange = i+3 >= mergeRanges.size(); 385 final boolean lastRange = i+3 >= mergeRanges.size();
352 final int s1LinesLeft = s1TotalLines - s1ConsumedLines; 386 final int s1LinesLeft = s1TotalLines - s1ConsumedLines;
353 // how many lines we may reported as changed (don't use more than in range unless it's the very last range) 387 // how many lines we may reported as changed (don't use more than in range unless it's the very last range)
354 final int s1LinesToBorrow = lastRange ? s1LinesLeft : Math.min(s1LinesLeft, rangeLen); 388 final int s1LinesToBorrow = lastRange ? s1LinesLeft : Math.min(s1LinesLeft, rangeLen);
355 if (s1LinesToBorrow > 0) { 389 if (s1LinesToBorrow > 0) {
356 BlockImpl2 block = new BlockImpl2(seq1, seq2, s1Start, s1LinesToBorrow, rangeStart, rangeLen, s1Start, rangeStart); 390 ChangeBlockImpl block = new ChangeBlockImpl(originContent, targetContent, s1Start, s1LinesToBorrow, rangeStart, rangeLen, s1Start, rangeStart);
357 block.setOriginAndTarget(rangeOrigin, csetTarget); 391 block.setOriginAndTarget(rangeOrigin, csetTarget);
358 insp.changed(block); 392 insp.changed(block);
359 s1ConsumedLines += s1LinesToBorrow; 393 s1ConsumedLines += s1LinesToBorrow;
360 s1Start += s1LinesToBorrow; 394 s1Start += s1LinesToBorrow;
361 } else { 395 } else {
362 BlockImpl2 block = getAddBlock(rangeStart, rangeLen, s1Start); 396 ChangeBlockImpl block = getAddBlock(rangeStart, rangeLen, s1Start);
363 block.setOriginAndTarget(rangeOrigin, csetTarget); 397 block.setOriginAndTarget(rangeOrigin, csetTarget);
364 insp.added(block); 398 insp.added(block);
365 } 399 }
366 } 400 }
367 if (s1ConsumedLines != s1TotalLines) { 401 if (s1ConsumedLines != s1TotalLines) {
368 throw new HgInvalidStateException(String.format("Expected to process %d lines, but actually was %d", s1TotalLines, s1ConsumedLines)); 402 throw new HgInvalidStateException(String.format("Expected to process %d lines, but actually was %d", s1TotalLines, s1ConsumedLines));
369 } 403 }
370 } else { 404 } else {
371 BlockImpl2 block = new BlockImpl2(seq1, seq2, s1From, s1To-s1From, s2From, s2To - s2From, s1From, s2From); 405 ChangeBlockImpl block = new ChangeBlockImpl(originContent, targetContent, s1From, s1To-s1From, s2From, s2To - s2From, s1From, s2From);
372 block.setOriginAndTarget(csetOrigin, csetTarget); 406 block.setOriginAndTarget(csetOrigin, csetTarget);
373 insp.changed(block); 407 insp.changed(block);
374 } 408 }
375 } 409 }
376 410
382 int insPoint = s1InsertPoint; // track changes to insertion point 416 int insPoint = s1InsertPoint; // track changes to insertion point
383 for (int i = 0; i < mergeRanges.size(); i += 3) { 417 for (int i = 0; i < mergeRanges.size(); i += 3) {
384 int rangeOrigin = mergeRanges.get(i); 418 int rangeOrigin = mergeRanges.get(i);
385 int rangeStart = mergeRanges.get(i+1); 419 int rangeStart = mergeRanges.get(i+1);
386 int rangeLen = mergeRanges.get(i+2); 420 int rangeLen = mergeRanges.get(i+2);
387 BlockImpl2 block = getAddBlock(rangeStart, rangeLen, insPoint); 421 ChangeBlockImpl block = getAddBlock(rangeStart, rangeLen, insPoint);
388 block.setOriginAndTarget(rangeOrigin, csetTarget); 422 block.setOriginAndTarget(rangeOrigin, csetTarget);
389 insp.added(block); 423 insp.added(block);
390 // indicate insPoint moved down number of lines we just reported 424 // indicate insPoint moved down number of lines we just reported
391 insPoint += rangeLen; 425 insPoint += rangeLen;
392 } 426 }
393 } else { 427 } else {
394 BlockImpl2 block = getAddBlock(s2From, s2To - s2From, s1InsertPoint); 428 ChangeBlockImpl block = getAddBlock(s2From, s2To - s2From, s1InsertPoint);
395 block.setOriginAndTarget(csetOrigin, csetTarget); 429 block.setOriginAndTarget(csetOrigin, csetTarget);
396 insp.added(block); 430 insp.added(block);
397 } 431 }
398 } 432 }
399 433
400 @Override 434 @Override
401 protected void deleted(int s2DeletePoint, int s1From, int s1To) { 435 protected void deleted(int s2DeletePoint, int s1From, int s1To) {
402 BlockImpl2 block = new BlockImpl2(seq1, null, s1From, s1To - s1From, -1, -1, -1, s2DeletePoint); 436 ChangeBlockImpl block = new ChangeBlockImpl(originContent, null, s1From, s1To - s1From, -1, -1, -1, s2DeletePoint);
403 block.setOriginAndTarget(csetOrigin, csetTarget); 437 block.setOriginAndTarget(csetOrigin, csetTarget);
404 insp.deleted(block); 438 insp.deleted(block);
405 } 439 }
406 440
407 @Override 441 @Override
408 protected void unchanged(int s1From, int s2From, int length) { 442 protected void unchanged(int s1From, int s2From, int length) {
409 BlockImpl1 block = new BlockImpl1(s1From, s2From, length); 443 EqualBlockImpl block = new EqualBlockImpl(s1From, s2From, length, targetContent);
410 block.setOriginAndTarget(csetOrigin, csetTarget); 444 block.setOriginAndTarget(csetOrigin, csetTarget);
411 insp.same(block); 445 insp.same(block);
412 } 446 }
413 447
414 private BlockImpl2 getAddBlock(int start, int len, int insPoint) { 448 private ChangeBlockImpl getAddBlock(int start, int len, int insPoint) {
415 return new BlockImpl2(null, seq2, -1, -1, start, len, insPoint, -1); 449 return new ChangeBlockImpl(null, targetContent, -1, -1, start, len, insPoint, -1);
416 } 450 }
417 } 451 }
418 452
419 static class BlockImpl implements Block { 453 static class BlockImpl implements Block {
420 454
436 public int targetChangesetIndex() { 470 public int targetChangesetIndex() {
437 return targetCset; 471 return targetCset;
438 } 472 }
439 } 473 }
440 474
441 static class BlockImpl1 extends BlockImpl implements EqualBlock { 475 static class EqualBlockImpl extends BlockImpl implements EqualBlock {
442 private final int start1, start2; 476 private final int start1, start2;
443 private final int length; 477 private final int length;
444 478 private final ContentBlock fullContent;
445 BlockImpl1(int blockStartSeq1, int blockStartSeq2, int blockLength) { 479 private FilterBlock myContent;
480
481 EqualBlockImpl(int blockStartSeq1, int blockStartSeq2, int blockLength, ContentBlock targetContent) {
446 start1 = blockStartSeq1; 482 start1 = blockStartSeq1;
447 start2 = blockStartSeq2; 483 start2 = blockStartSeq2;
448 length = blockLength; 484 length = blockLength;
485 fullContent = targetContent;
449 } 486 }
450 487
451 public int originStart() { 488 public int originStart() {
452 return start1; 489 return start1;
453 } 490 }
456 return start2; 493 return start2;
457 } 494 }
458 495
459 public int length() { 496 public int length() {
460 return length; 497 return length;
498 }
499
500 public BlockData content() {
501 if (myContent == null) {
502 myContent = new FilterBlock(fullContent, start2, length);
503 }
504 return myContent;
461 } 505 }
462 506
463 @Override 507 @Override
464 public String toString() { 508 public String toString() {
465 return String.format("@@ [%d..%d) == [%d..%d) @@", start1, start1+length, start2, start2+length); 509 return String.format("@@ [%d..%d) == [%d..%d) @@", start1, start1+length, start2, start2+length);
466 } 510 }
467 } 511 }
468 512
469 static class BlockImpl2 extends BlockImpl implements ChangeBlock { 513 static class ChangeBlockImpl extends BlockImpl implements ChangeBlock {
470 514
471 private final LineSequence oldSeq; 515 private final ContentBlock oldContent;
472 private final LineSequence newSeq; 516 private final ContentBlock newContent;
473 private final int s1Start; 517 private final int s1Start;
474 private final int s1Len; 518 private final int s1Len;
475 private final int s2Start; 519 private final int s2Start;
476 private final int s2Len; 520 private final int s2Len;
477 private final int s1InsertPoint; 521 private final int s1InsertPoint;
478 private final int s2DeletePoint; 522 private final int s2DeletePoint;
479 523 private FilterBlock addedBlock, removedBlock;
480 public BlockImpl2(LineSequence s1, LineSequence s2, int s1Start, int s1Len, int s2Start, int s2Len, int s1InsertPoint, int s2DeletePoint) { 524
481 oldSeq = s1; 525 public ChangeBlockImpl(ContentBlock c1, ContentBlock c2, int s1Start, int s1Len, int s2Start, int s2Len, int s1InsertPoint, int s2DeletePoint) {
482 newSeq = s2; 526 oldContent = c1;
527 newContent = c2;
483 this.s1Start = s1Start; 528 this.s1Start = s1Start;
484 this.s1Len = s1Len; 529 this.s1Len = s1Len;
485 this.s2Start = s2Start; 530 this.s2Start = s2Start;
486 this.s2Len = s2Len; 531 this.s2Len = s2Len;
487 this.s1InsertPoint = s1InsertPoint; 532 this.s1InsertPoint = s1InsertPoint;
498 543
499 public int totalAddedLines() { 544 public int totalAddedLines() {
500 return s2Len; 545 return s2Len;
501 } 546 }
502 547
503 public String[] addedLines() { 548 public BlockData addedLines() {
504 return generateLines(totalAddedLines(), firstAddedLine()); 549 if (addedBlock == null) {
550 addedBlock = new FilterBlock(newContent, firstAddedLine(), totalAddedLines());
551 }
552 return addedBlock;
505 } 553 }
506 554
507 public int removedAt() { 555 public int removedAt() {
508 return s2DeletePoint; 556 return s2DeletePoint;
509 } 557 }
514 562
515 public int totalRemovedLines() { 563 public int totalRemovedLines() {
516 return s1Len; 564 return s1Len;
517 } 565 }
518 566
519 public String[] removedLines() { 567 public BlockData removedLines() {
520 return generateLines(totalRemovedLines(), firstRemovedLine()); 568 if (removedBlock == null) {
521 } 569 removedBlock = new FilterBlock(oldContent, firstRemovedLine(), totalRemovedLines());
522 570 }
523 private String[] generateLines(int count, int startFrom) { 571 return removedBlock;
524 String[] rv = new String[count];
525 for (int i = 0; i < count; i++) {
526 rv[i] = String.format("LINE %d", startFrom + i+1);
527 }
528 return rv;
529 } 572 }
530 573
531 @Override 574 @Override
532 public String toString() { 575 public String toString() {
533 if (s2DeletePoint == -1) { 576 if (s2DeletePoint == -1) {
537 return String.format("@@ -%d,%d +%d,0 @@", firstRemovedLine(), totalRemovedLines(), removedAt()); 580 return String.format("@@ -%d,%d +%d,0 @@", firstRemovedLine(), totalRemovedLines(), removedAt());
538 } 581 }
539 return String.format("@@ -%d,%d +%d,%d @@", firstRemovedLine(), totalRemovedLines(), firstAddedLine(), totalAddedLines()); 582 return String.format("@@ -%d,%d +%d,%d @@", firstRemovedLine(), totalRemovedLines(), firstAddedLine(), totalAddedLines());
540 } 583 }
541 } 584 }
585
586 private static class SingleLine implements BlockData {
587 private final ByteChain line;
588
589 public SingleLine(ByteChain lineContent) {
590 line = lineContent;
591 }
592
593 public BlockData elementAt(int index) {
594 assert false;
595 return null;
596 }
597
598 public int elementCount() {
599 return 0;
600 }
601
602 public byte[] asArray() {
603 return line.data();
604 }
605 }
606
607 private static class ContentBlock implements BlockData {
608 private final LineSequence seq;
609
610 public ContentBlock(LineSequence sequence) {
611 seq = sequence;
612 }
613
614 public BlockData elementAt(int index) {
615 return new SingleLine(seq.chunk(index));
616 }
617
618 public int elementCount() {
619 return seq.chunkCount() - 1;
620 }
621
622 public byte[] asArray() {
623 return seq.data(0, seq.chunkCount() - 1);
624 }
625 }
626
627 private static class FilterBlock implements BlockData {
628 private final ContentBlock contentBlock;
629 private final int from;
630 private final int length;
631
632 public FilterBlock(ContentBlock bd, int startFrom, int len) {
633 assert bd != null;
634 assert startFrom + len < bd.seq.chunkCount(); // there's one extra chunk in the end, so strict less is ok
635 contentBlock = bd;
636 from = startFrom;
637 length = len;
638 }
639
640 public BlockData elementAt(int index) {
641 if (index < 0 || index >= length) {
642 throw new IllegalArgumentException(String.format("Expected value from [0..%d), got %d", length, index));
643 }
644 return contentBlock.elementAt(from + index);
645 }
646
647 public int elementCount() {
648 return length;
649 }
650
651 public byte[] asArray() {
652 return contentBlock.seq.data(from, from + length);
653 }
654 }
655
542 656
543 static class EqualBlocksCollector implements DiffHelper.MatchInspector<LineSequence> { 657 static class EqualBlocksCollector implements DiffHelper.MatchInspector<LineSequence> {
544 private final IntVector matches = new IntVector(10*3, 2*3); 658 private final IntVector matches = new IntVector(10*3, 2*3);
545 659
546 public void begin(LineSequence s1, LineSequence s2) { 660 public void begin(LineSequence s1, LineSequence s2) {