/** * Checks that second CloneGroup includes first one. * * <p>CloneGroup A is included in another CloneGroup B, if every part pA from A has part pB in B, * which satisfy the conditions: * * <pre> * (pA.resourceId == pB.resourceId) and (pB.unitStart <= pA.unitStart) and (pA.unitEnd <= pB.unitEnd) * </pre> * * And all resourcesId from B exactly the same as all resourceId from A, which means that also * every part pB from B has part pA in A, which satisfy the condition: * * <pre> * pB.resourceId == pA.resourceId * </pre> * * Inclusion is the partial order, thus this relation is: * * <ul> * <li>reflexive - A in A * <li>transitive - (A in B) and (B in C) => (A in C) * <li>antisymmetric - (A in B) and (B in A) <=> (A = B) * </ul> * * <p>This method uses the fact that all parts already sorted by resourceId and unitStart (see * {@link ContainsInComparator#CLONEPART_COMPARATOR}), so running time - O(|A|+|B|). */ private static boolean containsIn(CloneGroup first, CloneGroup second) { List<ClonePart> firstParts = first.getCloneParts(); List<ClonePart> secondParts = second.getCloneParts(); // TODO Godin: according to tests seems that if first part of condition is true, then second // part can not be false // if this can be proved, then second part can be removed return SortedListsUtils.contains( secondParts, firstParts, new ContainsInComparator(second.getCloneUnitLength(), first.getCloneUnitLength())) && SortedListsUtils.contains( firstParts, secondParts, ContainsInComparator.RESOURCE_ID_COMPARATOR); }
/** * Given file with two lines, containing following statements: * * <pre> * 0: A,B,A,B * 1: A,B,A * </pre> * * with block size 5 each block will span both lines, and hashes will be: * * <pre> * A,B,A,B,A=1 * B,A,B,A,B=2 * A,B,A,B,A=1 * </pre> * * Expected: one clone with two parts, which contain exactly the same lines */ @Test public void same_lines_but_different_indexes() { CloneIndex cloneIndex = createIndex(); Block.Builder block = Block.builder().setResourceId("a").setLines(0, 1); Block[] fileBlocks = new Block[] { block.setBlockHash(new ByteArray("1".getBytes())).setIndexInFile(0).build(), block.setBlockHash(new ByteArray("2".getBytes())).setIndexInFile(1).build(), block.setBlockHash(new ByteArray("1".getBytes())).setIndexInFile(2).build() }; List<CloneGroup> clones = detect(cloneIndex, fileBlocks); print(clones); assertThat(clones.size(), is(1)); Iterator<CloneGroup> clonesIterator = clones.iterator(); CloneGroup clone = clonesIterator.next(); assertThat(clone.getCloneUnitLength(), is(1)); assertThat(clone.getCloneParts().size(), is(2)); assertThat(clone.getOriginPart(), is(new ClonePart("a", 0, 0, 1))); assertThat(clone.getCloneParts(), hasItem(new ClonePart("a", 0, 0, 1))); assertThat(clone.getCloneParts(), hasItem(new ClonePart("a", 2, 0, 1))); }