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