Mercurial > jhg
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) { |
