Mercurial > hg4j
comparison src/org/tmatesoft/hg/internal/AnnotateFacility.java @ 549:83afa680555d
Annotate merge revision (combined diff against two parents without looking further)
| author | Artem Tikhomirov <tikhomirov.artem@gmail.com> |
|---|---|
| date | Tue, 19 Feb 2013 21:17:39 +0100 |
| parents | ab21ac7dd833 |
| children | 4ea0351ca878 |
comparison
equal
deleted
inserted
replaced
| 548:ab21ac7dd833 | 549:83afa680555d |
|---|---|
| 21 | 21 |
| 22 import org.tmatesoft.hg.core.Nodeid; | 22 import org.tmatesoft.hg.core.Nodeid; |
| 23 import org.tmatesoft.hg.internal.PatchGenerator.LineSequence; | 23 import org.tmatesoft.hg.internal.PatchGenerator.LineSequence; |
| 24 import org.tmatesoft.hg.repo.HgDataFile; | 24 import org.tmatesoft.hg.repo.HgDataFile; |
| 25 import org.tmatesoft.hg.repo.HgInvalidStateException; | 25 import org.tmatesoft.hg.repo.HgInvalidStateException; |
| 26 import org.tmatesoft.hg.repo.HgRepository; | |
| 27 import org.tmatesoft.hg.util.CancelledException; | 26 import org.tmatesoft.hg.util.CancelledException; |
| 28 | 27 |
| 29 /** | 28 /** |
| 30 * | 29 * |
| 31 * @author Artem Tikhomirov | 30 * @author Artem Tikhomirov |
| 32 * @author TMate Software Ltd. | 31 * @author TMate Software Ltd. |
| 33 */ | 32 */ |
| 34 @Experimental(reason="work in progress") | 33 @Experimental(reason="work in progress") |
| 35 public class AnnotateFacility { | 34 public class AnnotateFacility { |
| 35 | |
| 36 /** | |
| 37 * mimic 'hg diff -r csetRevIndex1 -r csetRevIndex2' | |
| 38 */ | |
| 39 public void diff(HgDataFile df, int csetRevIndex1, int csetRevIndex2, BlockInspector insp) { | |
| 40 int fileRevIndex1 = fileRevIndex(df, csetRevIndex1); | |
| 41 int fileRevIndex2 = fileRevIndex(df, csetRevIndex2); | |
| 42 LineSequence c1 = lines(df, fileRevIndex1); | |
| 43 LineSequence c2 = lines(df, fileRevIndex2); | |
| 44 PatchGenerator<LineSequence> pg = new PatchGenerator<LineSequence>(); | |
| 45 pg.init(c1, c2); | |
| 46 pg.findMatchingBlocks(new BlameBlockInspector(insp, csetRevIndex1, csetRevIndex2)); | |
| 47 } | |
| 36 | 48 |
| 37 /** | 49 /** |
| 38 * Annotate file revision, line by line. | 50 * Annotate file revision, line by line. |
| 39 */ | 51 */ |
| 40 public void annotate(HgDataFile df, int changesetRevisionIndex, LineInspector insp) { | 52 public void annotate(HgDataFile df, int changesetRevisionIndex, LineInspector insp) { |
| 41 if (!df.exists()) { | 53 if (!df.exists()) { |
| 42 return; | 54 return; |
| 43 } | 55 } |
| 44 Nodeid fileRev = df.getRepo().getManifest().getFileRevision(changesetRevisionIndex, df.getPath()); | 56 int fileRevIndex = fileRevIndex(df, changesetRevisionIndex); |
| 45 int fileRevIndex = df.getRevisionIndex(fileRev); | |
| 46 int[] fileRevParents = new int[2]; | 57 int[] fileRevParents = new int[2]; |
| 47 FileAnnotation fa = new FileAnnotation(insp); | 58 FileAnnotation fa = new FileAnnotation(insp); |
| 48 do { | 59 do { |
| 49 // also covers changesetRevisionIndex == TIP, #implAnnotateChange doesn't tolerate constants | 60 // also covers changesetRevisionIndex == TIP, #implAnnotateChange doesn't tolerate constants |
| 50 changesetRevisionIndex = df.getChangesetRevisionIndex(fileRevIndex); | 61 changesetRevisionIndex = df.getChangesetRevisionIndex(fileRevIndex); |
| 57 /** | 68 /** |
| 58 * Annotates changes of the file against its parent(s) | 69 * Annotates changes of the file against its parent(s) |
| 59 */ | 70 */ |
| 60 public void annotateChange(HgDataFile df, int changesetRevisionIndex, BlockInspector insp) { | 71 public void annotateChange(HgDataFile df, int changesetRevisionIndex, BlockInspector insp) { |
| 61 // TODO detect if file is text/binary (e.g. looking for chars < ' ' and not \t\r\n\f | 72 // TODO detect if file is text/binary (e.g. looking for chars < ' ' and not \t\r\n\f |
| 62 Nodeid fileRev = df.getRepo().getManifest().getFileRevision(changesetRevisionIndex, df.getPath()); | 73 int fileRevIndex = fileRevIndex(df, changesetRevisionIndex); |
| 63 int fileRevIndex = df.getRevisionIndex(fileRev); | |
| 64 int[] fileRevParents = new int[2]; | 74 int[] fileRevParents = new int[2]; |
| 65 df.parents(fileRevIndex, fileRevParents, null, null); | 75 df.parents(fileRevIndex, fileRevParents, null, null); |
| 66 if (changesetRevisionIndex == TIP) { | 76 if (changesetRevisionIndex == TIP) { |
| 67 changesetRevisionIndex = df.getChangesetRevisionIndex(fileRevIndex); | 77 changesetRevisionIndex = df.getChangesetRevisionIndex(fileRevIndex); |
| 68 } | 78 } |
| 69 implAnnotateChange(df, changesetRevisionIndex, fileRevIndex, fileRevParents, insp); | 79 implAnnotateChange(df, changesetRevisionIndex, fileRevIndex, fileRevParents, insp); |
| 70 } | 80 } |
| 71 | 81 |
| 72 private void implAnnotateChange(HgDataFile df, int csetRevIndex, int fileRevIndex, int[] fileParentRevs, BlockInspector insp) { | 82 private void implAnnotateChange(HgDataFile df, int csetRevIndex, int fileRevIndex, int[] fileParentRevs, BlockInspector insp) { |
| 83 final LineSequence fileRevLines = lines(df, fileRevIndex); | |
| 84 if (fileParentRevs[0] != NO_REVISION && fileParentRevs[1] != NO_REVISION) { | |
| 85 LineSequence p1Lines = lines(df, fileParentRevs[0]); | |
| 86 LineSequence p2Lines = lines(df, fileParentRevs[1]); | |
| 87 int p1ClogIndex = df.getChangesetRevisionIndex(fileParentRevs[0]); | |
| 88 int p2ClogIndex = df.getChangesetRevisionIndex(fileParentRevs[1]); | |
| 89 PatchGenerator<LineSequence> pg = new PatchGenerator<LineSequence>(); | |
| 90 pg.init(p2Lines, fileRevLines); | |
| 91 EqualBlocksCollector p2MergeCommon = new EqualBlocksCollector(); | |
| 92 pg.findMatchingBlocks(p2MergeCommon); | |
| 93 // | |
| 94 pg.init(p1Lines); | |
| 95 BlameBlockInspector bbi = new BlameBlockInspector(insp, p1ClogIndex, csetRevIndex); | |
| 96 bbi.setMergeParent2(p2MergeCommon, p2ClogIndex); | |
| 97 pg.findMatchingBlocks(bbi); | |
| 98 } else if (fileParentRevs[0] == fileParentRevs[1]) { | |
| 99 // may be equal iff both are unset | |
| 100 assert fileParentRevs[0] == NO_REVISION; | |
| 101 // everything added | |
| 102 BlameBlockInspector bbi = new BlameBlockInspector(insp, NO_REVISION, csetRevIndex); | |
| 103 bbi.begin(LineSequence.newlines(new byte[0]), fileRevLines); | |
| 104 bbi.match(0, fileRevLines.chunkCount()-1, 0); | |
| 105 bbi.end(); | |
| 106 } else { | |
| 107 int soleParent = fileParentRevs[0] == NO_REVISION ? fileParentRevs[1] : fileParentRevs[0]; | |
| 108 assert soleParent != NO_REVISION; | |
| 109 LineSequence parentLines = lines(df, soleParent); | |
| 110 | |
| 111 int parentChangesetRevIndex = df.getChangesetRevisionIndex(soleParent); | |
| 112 PatchGenerator<LineSequence> pg = new PatchGenerator<LineSequence>(); | |
| 113 pg.init(parentLines, fileRevLines); | |
| 114 pg.findMatchingBlocks(new BlameBlockInspector(insp, parentChangesetRevIndex, csetRevIndex)); | |
| 115 } | |
| 116 } | |
| 117 | |
| 118 private static int fileRevIndex(HgDataFile df, int csetRevIndex) { | |
| 119 Nodeid fileRev = df.getRepo().getManifest().getFileRevision(csetRevIndex, df.getPath()); | |
| 120 return df.getRevisionIndex(fileRev); | |
| 121 } | |
| 122 | |
| 123 private static LineSequence lines(HgDataFile df, int fileRevIndex) { | |
| 73 try { | 124 try { |
| 74 if (fileParentRevs[0] != NO_REVISION && fileParentRevs[1] != NO_REVISION) { | 125 ByteArrayChannel c; |
| 75 // merge | 126 df.content(fileRevIndex, c = new ByteArrayChannel()); |
| 76 } else if (fileParentRevs[0] == fileParentRevs[1]) { | 127 return LineSequence.newlines(c.toArray()); |
| 77 // may be equal iff both are unset | |
| 78 assert fileParentRevs[0] == NO_REVISION; | |
| 79 // everything added | |
| 80 ByteArrayChannel c; | |
| 81 df.content(fileRevIndex, c = new ByteArrayChannel()); | |
| 82 BlameBlockInspector bbi = new BlameBlockInspector(insp, NO_REVISION, csetRevIndex); | |
| 83 LineSequence cls = LineSequence.newlines(c.toArray()); | |
| 84 bbi.begin(LineSequence.newlines(new byte[0]), cls); | |
| 85 bbi.match(0, cls.chunkCount()-1, 0); | |
| 86 bbi.end(); | |
| 87 } else { | |
| 88 int soleParent = fileParentRevs[0] == NO_REVISION ? fileParentRevs[1] : fileParentRevs[0]; | |
| 89 assert soleParent != NO_REVISION; | |
| 90 ByteArrayChannel c1, c2; | |
| 91 df.content(soleParent, c1 = new ByteArrayChannel()); | |
| 92 df.content(fileRevIndex, c2 = new ByteArrayChannel()); | |
| 93 int parentChangesetRevIndex = df.getChangesetRevisionIndex(soleParent); | |
| 94 PatchGenerator<LineSequence> pg = new PatchGenerator<LineSequence>(); | |
| 95 pg.init(LineSequence.newlines(c1.toArray()), LineSequence.newlines(c2.toArray())); | |
| 96 pg.findMatchingBlocks(new BlameBlockInspector(insp, parentChangesetRevIndex, csetRevIndex)); | |
| 97 } | |
| 98 } catch (CancelledException ex) { | 128 } catch (CancelledException ex) { |
| 99 // TODO likely it was bad idea to throw cancelled exception from content() | 129 // TODO likely it was bad idea to throw cancelled exception from content() |
| 100 // deprecate and provide alternative? | 130 // deprecate and provide alternative? |
| 101 HgInvalidStateException ise = new HgInvalidStateException("ByteArrayChannel never throws CancelledException"); | 131 HgInvalidStateException ise = new HgInvalidStateException("ByteArrayChannel never throws CancelledException"); |
| 102 ise.initCause(ex); | 132 ise.initCause(ex); |
| 164 | 194 |
| 165 | 195 |
| 166 | 196 |
| 167 static class BlameBlockInspector extends PatchGenerator.DeltaInspector<LineSequence> { | 197 static class BlameBlockInspector extends PatchGenerator.DeltaInspector<LineSequence> { |
| 168 private final BlockInspector insp; | 198 private final BlockInspector insp; |
| 169 private final int csetP1; | 199 private final int csetOrigin; |
| 170 private final int csetTarget; | 200 private final int csetTarget; |
| 171 | 201 private EqualBlocksCollector p2MergeCommon; |
| 172 public BlameBlockInspector(BlockInspector inspector, int parentCset1, int targetCset) { | 202 private int csetMergeParent; |
| 203 private IntVector mergeRanges; | |
| 204 | |
| 205 public BlameBlockInspector(BlockInspector inspector, int originCset, int targetCset) { | |
| 173 assert inspector != null; | 206 assert inspector != null; |
| 174 insp = inspector; | 207 insp = inspector; |
| 175 csetP1 = parentCset1; | 208 csetOrigin = originCset; |
| 176 csetTarget = targetCset; | 209 csetTarget = targetCset; |
| 210 } | |
| 211 | |
| 212 public void setMergeParent2(EqualBlocksCollector p2Merge, int parentCset2) { | |
| 213 p2MergeCommon = p2Merge; | |
| 214 csetMergeParent = parentCset2; | |
| 215 mergeRanges = new IntVector(3*10, 3*10); | |
| 177 } | 216 } |
| 178 | 217 |
| 179 @Override | 218 @Override |
| 180 public void begin(LineSequence s1, LineSequence s2) { | 219 public void begin(LineSequence s1, LineSequence s2) { |
| 181 super.begin(s1, s2); | 220 super.begin(s1, s2); |
| 192 } | 231 } |
| 193 } | 232 } |
| 194 | 233 |
| 195 @Override | 234 @Override |
| 196 protected void changed(int s1From, int s1To, int s2From, int s2To) { | 235 protected void changed(int s1From, int s1To, int s2From, int s2To) { |
| 197 BlockImpl2 block = new BlockImpl2(seq1, seq2, s1From, s1To-s1From, s2From, s2To - s2From, s1From, s2From); | 236 if (p2MergeCommon != null) { |
| 198 block.setOriginAndTarget(csetP1, csetTarget); | 237 mergeRanges.clear(); |
| 199 insp.changed(block); | 238 p2MergeCommon.combineAndMarkRangesWithTarget(s2From, s2To - s2From, csetOrigin, csetMergeParent, mergeRanges); |
| 239 | |
| 240 /* | |
| 241 * Usecases: | |
| 242 * 3 lines changed to 10 lines. range of 10 lines breaks down to 2 from p2, 3 from p1, and 5 from p2. | |
| 243 * We report: 2 lines changed to 2(p2), then 1 line changed with 3(p1) and 5 lines added from p2. | |
| 244 * | |
| 245 * 10 lines changed to 3 lines, range of 3 lines breaks down to 2 line from p1 and 1 line from p2. | |
| 246 * We report: 2 lines changed to 2(p1) and 8 lines changed to 1(p2) | |
| 247 */ | |
| 248 int s1TotalLines = s1To - s1From, s1ConsumedLines = 0, s1Start = s1From; | |
| 249 | |
| 250 for (int i = 0; i < mergeRanges.size(); i += 3) { | |
| 251 final int rangeOrigin = mergeRanges.get(i); | |
| 252 final int rangeStart = mergeRanges.get(i+1); | |
| 253 final int rangeLen = mergeRanges.get(i+2); | |
| 254 final boolean lastRange = i+3 >= mergeRanges.size(); | |
| 255 final int s1LinesLeft = s1TotalLines - s1ConsumedLines; | |
| 256 // how many lines we may reported as changed (don't use more than in range unless it's the very last range) | |
| 257 final int s1LinesToBorrow = lastRange ? s1LinesLeft : Math.min(s1LinesLeft, rangeLen); | |
| 258 if (s1LinesToBorrow > 0) { | |
| 259 BlockImpl2 block = new BlockImpl2(seq1, seq2, s1Start, s1LinesToBorrow, rangeStart, rangeLen, s1Start, rangeStart); | |
| 260 block.setOriginAndTarget(rangeOrigin, csetTarget); | |
| 261 insp.changed(block); | |
| 262 s1ConsumedLines += s1LinesToBorrow; | |
| 263 s1Start += s1LinesToBorrow; | |
| 264 } else { | |
| 265 BlockImpl2 block = getAddBlock(rangeStart, rangeLen, s1Start); | |
| 266 block.setOriginAndTarget(rangeOrigin, csetTarget); | |
| 267 insp.added(block); | |
| 268 } | |
| 269 } | |
| 270 if (s1ConsumedLines != s1TotalLines) { | |
| 271 throw new HgInvalidStateException(String.format("Expected to process %d lines, but actually was %d", s1TotalLines, s1ConsumedLines)); | |
| 272 } | |
| 273 } else { | |
| 274 BlockImpl2 block = new BlockImpl2(seq1, seq2, s1From, s1To-s1From, s2From, s2To - s2From, s1From, s2From); | |
| 275 block.setOriginAndTarget(csetOrigin, csetTarget); | |
| 276 insp.changed(block); | |
| 277 } | |
| 200 } | 278 } |
| 201 | 279 |
| 202 @Override | 280 @Override |
| 203 protected void added(int s1InsertPoint, int s2From, int s2To) { | 281 protected void added(int s1InsertPoint, int s2From, int s2To) { |
| 204 BlockImpl2 block = new BlockImpl2(null, seq2, -1, -1, s2From, s2To - s2From, s1InsertPoint, -1); | 282 if (p2MergeCommon != null) { |
| 205 block.setOriginAndTarget(csetP1, csetTarget); | 283 mergeRanges.clear(); |
| 206 insp.added(block); | 284 p2MergeCommon.combineAndMarkRangesWithTarget(s2From, s2To - s2From, csetOrigin, csetMergeParent, mergeRanges); |
| 285 int insPoint = s1InsertPoint; // track changes to insertion point | |
| 286 for (int i = 0; i < mergeRanges.size(); i += 3) { | |
| 287 int rangeOrigin = mergeRanges.get(i); | |
| 288 int rangeStart = mergeRanges.get(i+1); | |
| 289 int rangeLen = mergeRanges.get(i+2); | |
| 290 BlockImpl2 block = getAddBlock(rangeStart, rangeLen, insPoint); | |
| 291 block.setOriginAndTarget(rangeOrigin, csetTarget); | |
| 292 insp.added(block); | |
| 293 // indicate insPoint moved down number of lines we just reported | |
| 294 insPoint += rangeLen; | |
| 295 } | |
| 296 } else { | |
| 297 BlockImpl2 block = getAddBlock(s2From, s2To - s2From, s1InsertPoint); | |
| 298 block.setOriginAndTarget(csetOrigin, csetTarget); | |
| 299 insp.added(block); | |
| 300 } | |
| 207 } | 301 } |
| 208 | 302 |
| 209 @Override | 303 @Override |
| 210 protected void deleted(int s2DeletePoint, int s1From, int s1To) { | 304 protected void deleted(int s2DeletePoint, int s1From, int s1To) { |
| 211 BlockImpl2 block = new BlockImpl2(seq1, null, s1From, s1To - s1From, -1, -1, -1, s2DeletePoint); | 305 BlockImpl2 block = new BlockImpl2(seq1, null, s1From, s1To - s1From, -1, -1, -1, s2DeletePoint); |
| 212 block.setOriginAndTarget(csetP1, csetTarget); | 306 block.setOriginAndTarget(csetOrigin, csetTarget); |
| 213 insp.deleted(block); | 307 insp.deleted(block); |
| 214 } | 308 } |
| 215 | 309 |
| 216 @Override | 310 @Override |
| 217 protected void unchanged(int s1From, int s2From, int length) { | 311 protected void unchanged(int s1From, int s2From, int length) { |
| 218 BlockImpl1 block = new BlockImpl1(s1From, s2From, length); | 312 BlockImpl1 block = new BlockImpl1(s1From, s2From, length); |
| 219 block.setOriginAndTarget(csetP1, csetTarget); | 313 block.setOriginAndTarget(csetOrigin, csetTarget); |
| 220 insp.same(block); | 314 insp.same(block); |
| 315 } | |
| 316 | |
| 317 private BlockImpl2 getAddBlock(int start, int len, int insPoint) { | |
| 318 return new BlockImpl2(null, seq2, -1, -1, start, len, insPoint, -1); | |
| 221 } | 319 } |
| 222 } | 320 } |
| 223 | 321 |
| 224 static class BlockImpl implements Block { | 322 static class BlockImpl implements Block { |
| 225 | 323 |
| 342 return String.format("@@ -%d,%d +%d,0 @@", firstRemovedLine(), totalRemovedLines(), removedAt()); | 440 return String.format("@@ -%d,%d +%d,0 @@", firstRemovedLine(), totalRemovedLines(), removedAt()); |
| 343 } | 441 } |
| 344 return String.format("@@ -%d,%d +%d,%d @@", firstRemovedLine(), totalRemovedLines(), firstAddedLine(), totalAddedLines()); | 442 return String.format("@@ -%d,%d +%d,%d @@", firstRemovedLine(), totalRemovedLines(), firstAddedLine(), totalAddedLines()); |
| 345 } | 443 } |
| 346 } | 444 } |
| 445 | |
| 446 static class EqualBlocksCollector implements PatchGenerator.MatchInspector<LineSequence> { | |
| 447 private final IntVector matches = new IntVector(10*3, 2*3); | |
| 448 | |
| 449 public void begin(LineSequence s1, LineSequence s2) { | |
| 450 } | |
| 451 | |
| 452 public void match(int startSeq1, int startSeq2, int matchLength) { | |
| 453 matches.add(startSeq1); | |
| 454 matches.add(startSeq2); | |
| 455 matches.add(matchLength); | |
| 456 } | |
| 457 | |
| 458 public void end() { | |
| 459 } | |
| 460 | |
| 461 // true when specified line in origin is equal to a line in target | |
| 462 public boolean includesOriginLine(int ln) { | |
| 463 return includes(ln, 0); | |
| 464 } | |
| 465 | |
| 466 // true when specified line in target is equal to a line in origin | |
| 467 public boolean includesTargetLine(int ln) { | |
| 468 return includes(ln, 1); | |
| 469 } | |
| 470 | |
| 471 public void intersectWithTarget(int start, int length, IntVector result) { | |
| 472 int s = start; | |
| 473 for (int l = start, x = start + length; l < x; l++) { | |
| 474 if (!includesTargetLine(l)) { | |
| 475 if (l - s > 0) { | |
| 476 result.add(s); | |
| 477 result.add(l - s); | |
| 478 } | |
| 479 s = l+1; | |
| 480 } | |
| 481 } | |
| 482 if (s < start+length) { | |
| 483 result.add(s); | |
| 484 result.add((start + length) - s); | |
| 485 } | |
| 486 } | |
| 487 | |
| 488 /* | |
| 489 * intersects [start..start+length) with ranges of target lines, and based on the intersection | |
| 490 * breaks initial range into smaller ranges and records them into result, with marker to indicate | |
| 491 * whether the range is from initial range (markerSource) or is a result of the intersection with target | |
| 492 * (markerTarget) | |
| 493 */ | |
| 494 public void combineAndMarkRangesWithTarget(int start, int length, int markerSource, int markerTarget, IntVector result) { | |
| 495 int sourceStart = start, targetStart = start, sourceEnd = start + length; | |
| 496 for (int l = sourceStart; l < sourceEnd; l++) { | |
| 497 if (includesTargetLine(l)) { | |
| 498 // l is from target | |
| 499 if (sourceStart < l) { | |
| 500 // few lines from source range were not in the target, report them | |
| 501 result.add(markerSource); | |
| 502 result.add(sourceStart); | |
| 503 result.add(l - sourceStart); | |
| 504 } | |
| 505 // indicate the earliest line from source range to use | |
| 506 sourceStart = l + 1; | |
| 507 } else { | |
| 508 // l is not in target | |
| 509 if (targetStart < l) { | |
| 510 // report lines from target range | |
| 511 result.add(markerTarget); | |
| 512 result.add(targetStart); | |
| 513 result.add(l - targetStart); | |
| 514 } | |
| 515 // next line *may* be from target | |
| 516 targetStart = l + 1; | |
| 517 } | |
| 518 } | |
| 519 // if source range end with line from target, sourceStart would be == sourceEnd, and we need to add range with markerTarget | |
| 520 // if source range doesn't end with target line, targetStart == sourceEnd, while sourceStart < sourceEnd | |
| 521 if (sourceStart < sourceEnd) { | |
| 522 assert targetStart == sourceEnd; | |
| 523 // something left from the source range | |
| 524 result.add(markerSource); | |
| 525 result.add(sourceStart); | |
| 526 result.add(sourceEnd - sourceStart); | |
| 527 } else if (targetStart < sourceEnd) { | |
| 528 assert sourceStart == sourceEnd; | |
| 529 result.add(markerTarget); | |
| 530 result.add(targetStart); | |
| 531 result.add(sourceEnd - targetStart); | |
| 532 } | |
| 533 } | |
| 534 | |
| 535 private boolean includes(int ln, int o) { | |
| 536 for (int i = 2; i < matches.size(); o += 3, i+=3) { | |
| 537 int rangeStart = matches.get(o); | |
| 538 if (rangeStart > ln) { | |
| 539 return false; | |
| 540 } | |
| 541 int rangeLen = matches.get(i); | |
| 542 if (rangeStart + rangeLen > ln) { | |
| 543 return true; | |
| 544 } | |
| 545 } | |
| 546 return false; | |
| 547 } | |
| 548 } | |
| 549 | |
| 550 public static void main(String[] args) { | |
| 551 EqualBlocksCollector bc = new EqualBlocksCollector(); | |
| 552 bc.match(-1, 5, 3); | |
| 553 bc.match(-1, 10, 2); | |
| 554 bc.match(-1, 15, 3); | |
| 555 bc.match(-1, 20, 3); | |
| 556 assert !bc.includesTargetLine(4); | |
| 557 assert bc.includesTargetLine(7); | |
| 558 assert !bc.includesTargetLine(8); | |
| 559 assert bc.includesTargetLine(10); | |
| 560 assert !bc.includesTargetLine(12); | |
| 561 IntVector r = new IntVector(); | |
| 562 bc.intersectWithTarget(7, 10, r); | |
| 563 for (int i = 0; i < r.size(); i+=2) { | |
| 564 System.out.printf("[%d..%d) ", r.get(i), r.get(i) + r.get(i+1)); | |
| 565 } | |
| 566 System.out.println(); | |
| 567 r.clear(); | |
| 568 bc.combineAndMarkRangesWithTarget(0, 16, 508, 514, r); | |
| 569 for (int i = 0; i < r.size(); i+=3) { | |
| 570 System.out.printf("%d:[%d..%d) ", r.get(i), r.get(i+1), r.get(i+1) + r.get(i+2)); | |
| 571 } | |
| 572 } | |
| 347 } | 573 } |
