/*
* SonarQube
* Copyright (C) 2009-2017 SonarSource SA
* mailto:info AT sonarsource DOT com
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 3 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public License
* along with this program; if not, write to the Free Software Foundation,
* Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
package org.sonar.duplications.detector.original;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import javax.annotation.CheckForNull;
import org.sonar.duplications.block.Block;
import org.sonar.duplications.utils.FastStringComparator;
/**
* Set of {@link Block}s, which internally stored as a sorted list.
*/
final class BlocksGroup {
/**
* Factory method.
*
* @return new empty group
*/
public static BlocksGroup empty() {
return new BlocksGroup();
}
protected final List<Block> blocks;
private BlocksGroup() {
this.blocks = new ArrayList<>();
}
public int size() {
return blocks.size();
}
/**
* @return true, if this group subsumed by specified group
* @see #subsumedBy(BlocksGroup, BlocksGroup, int)
*/
public boolean subsumedBy(BlocksGroup other, int indexCorrection) {
return subsumedBy(this, other, indexCorrection);
}
/**
* @return intersection of this group with specified
* @see #intersect(BlocksGroup, BlocksGroup)
*/
public BlocksGroup intersect(BlocksGroup other) {
return intersect(this, other);
}
public List<Block[]> pairs(BlocksGroup other, int len) {
return pairs(this, other, len);
}
/**
* First block from this group with specified resource id.
*/
@CheckForNull
public Block first(String resourceId) {
for (Block block : blocks) {
if (resourceId.equals(block.getResourceId())) {
return block;
}
}
return null;
}
@Override
public String toString() {
return blocks.toString();
}
/**
* Intersection of two groups is a group, which contains blocks from second group that have corresponding block from first group
* with same resource id and with corrected index.
*/
private static BlocksGroup intersect(BlocksGroup group1, BlocksGroup group2) {
BlocksGroup intersection = new BlocksGroup();
List<Block> list1 = group1.blocks;
List<Block> list2 = group2.blocks;
int i = 0;
int j = 0;
while (i < list1.size() && j < list2.size()) {
Block block1 = list1.get(i);
Block block2 = list2.get(j);
int c = RESOURCE_ID_COMPARATOR.compare(block1.getResourceId(), block2.getResourceId());
if (c > 0) {
j++;
continue;
}
if (c < 0) {
i++;
continue;
}
if (c == 0) {
c = block1.getIndexInFile() + 1 - block2.getIndexInFile();
}
if (c == 0) {
// list1[i] == list2[j]
i++;
j++;
intersection.blocks.add(block2);
}
if (c > 0) {
// list1[i] > list2[j]
j++;
}
if (c < 0) {
// list1[i] < list2[j]
i++;
}
}
return intersection;
}
/**
* One group is subsumed by another group, when each block from first group has corresponding block from second group
* with same resource id and with corrected index.
*/
private static boolean subsumedBy(BlocksGroup group1, BlocksGroup group2, int indexCorrection) {
List<Block> list1 = group1.blocks;
List<Block> list2 = group2.blocks;
int i = 0;
int j = 0;
while (i < list1.size() && j < list2.size()) {
Block block1 = list1.get(i);
Block block2 = list2.get(j);
int c = RESOURCE_ID_COMPARATOR.compare(block1.getResourceId(), block2.getResourceId());
if (c != 0) {
j++;
continue;
}
c = block1.getIndexInFile() - indexCorrection - block2.getIndexInFile();
if (c < 0) {
// list1[i] < list2[j]
break;
}
if (c != 0) {
// list1[i] != list2[j]
j++;
}
if (c == 0) {
// list1[i] == list2[j]
i++;
j++;
}
}
return i == list1.size();
}
private static List<Block[]> pairs(BlocksGroup beginGroup, BlocksGroup endGroup, int len) {
List<Block[]> result = new ArrayList<>();
List<Block> beginBlocks = beginGroup.blocks;
List<Block> endBlocks = endGroup.blocks;
int i = 0;
int j = 0;
while (i < beginBlocks.size() && j < endBlocks.size()) {
Block beginBlock = beginBlocks.get(i);
Block endBlock = endBlocks.get(j);
int c = RESOURCE_ID_COMPARATOR.compare(beginBlock.getResourceId(), endBlock.getResourceId());
if (c == 0) {
c = beginBlock.getIndexInFile() + len - 1 - endBlock.getIndexInFile();
}
if (c == 0) {
result.add(new Block[] {beginBlock, endBlock});
i++;
j++;
}
if (c > 0) {
j++;
}
if (c < 0) {
i++;
}
}
return result;
}
private static final Comparator<String> RESOURCE_ID_COMPARATOR = FastStringComparator.INSTANCE;
/**
* Compares {@link Block}s first using {@link Block#getResourceId() resource id} and then using {@link Block#getIndexInFile() index in file}.
*/
public static class BlockComparator implements Comparator<Block> {
public static final BlockComparator INSTANCE = new BlockComparator();
@Override
public int compare(Block b1, Block b2) {
int c = RESOURCE_ID_COMPARATOR.compare(b1.getResourceId(), b2.getResourceId());
if (c == 0) {
return b1.getIndexInFile() - b2.getIndexInFile();
}
return c;
}
}
}