--- /dev/null
+/*
+ * SonarQube, open source software quality management tool.
+ * Copyright (C) 2008-2014 SonarSource
+ * mailto:contact AT sonarsource DOT com
+ *
+ * SonarQube 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.
+ *
+ * SonarQube 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.server.computation.duplication;
+
+import java.util.Objects;
+import javax.annotation.Nullable;
+
+/**
+ * Given that:
+ * <ul>
+ * <li>some language plugins are able to multiple duplication for the same textblock but with a
+ * different starting and/or ending offset</li>
+ * <li>there is no way to distinguish these block from each other as the offsets (or any other
+ * information) is not sent in the analysis report</li>
+ * </ul>,
+ * we are uniquely (and blindly) identifying each original block reported in the analysis report.
+ */
+public class DetailedTextBlock extends TextBlock {
+ private final int id;
+ /**
+ * @throws IllegalArgumentException if {@code start} is 0 or less
+ * @throws IllegalStateException if {@code end} is less than {@code start}
+ */
+ public DetailedTextBlock(int id, int start, int end) {
+ super(start, end);
+ this.id = id;
+ }
+
+ @Override
+ public boolean equals(@Nullable Object o) {
+ if (this == o) {
+ return true;
+ }
+ if (o == null || getClass() != o.getClass()) {
+ return false;
+ }
+ if (!super.equals(o)) {
+ return false;
+ }
+ DetailedTextBlock that = (DetailedTextBlock) o;
+ return id == that.id;
+ }
+
+ @Override
+ public int hashCode() {
+ return Objects.hash(super.hashCode(), id);
+ }
+}
import org.sonar.server.computation.component.DepthTraversalTypeAwareCrawler;
import org.sonar.server.computation.component.TreeRootHolder;
import org.sonar.server.computation.component.TypeAwareVisitorAdapter;
+import org.sonar.server.computation.duplication.DetailedTextBlock;
import org.sonar.server.computation.duplication.DuplicationRepository;
import org.sonar.server.computation.duplication.TextBlock;
public void visitFile(Component file) {
CloseableIterator<BatchReport.Duplication> duplications = batchReportReader.readComponentDuplications(file.getReportAttributes().getRef());
try {
+ int idGenerator = 1;
while (duplications.hasNext()) {
- loadDuplications(file, duplications.next());
+ loadDuplications(file, duplications.next(), idGenerator++);
}
} finally {
duplications.close();
}).visit(treeRootHolder.getRoot());
}
- private void loadDuplications(Component file, BatchReport.Duplication duplication) {
- TextBlock original = convert(duplication.getOriginPosition());
+ private void loadDuplications(Component file, BatchReport.Duplication duplication, int id) {
+ DetailedTextBlock original = convert(duplication.getOriginPosition(), id);
for (BatchReport.Duplicate duplicate : duplication.getDuplicateList()) {
if (duplicate.hasOtherFileRef()) {
duplicationRepository.addDuplication(file, original, treeRootHolder.getComponentByRef(duplicate.getOtherFileRef()), convert(duplicate.getRange()));
private static TextBlock convert(BatchReport.TextRange textRange) {
return new TextBlock(textRange.getStartLine(), textRange.getEndLine());
}
+
+ private static DetailedTextBlock convert(BatchReport.TextRange textRange, int id) {
+ return new DetailedTextBlock(id, textRange.getStartLine(), textRange.getEndLine());
+ }
}
import org.sonar.server.computation.batch.TreeRootHolderRule;
import org.sonar.server.computation.component.Component;
import org.sonar.server.computation.component.VisitException;
+import org.sonar.server.computation.duplication.DetailedTextBlock;
import org.sonar.server.computation.duplication.Duplicate;
import org.sonar.server.computation.duplication.Duplication;
import org.sonar.server.computation.duplication.DuplicationRepositoryRule;
underTest.execute();
assertNoDuplication(FILE_1_REF);
- assertDuplications(FILE_2_REF, singleLineTextBlock(LINE), new InnerDuplicate(singleLineTextBlock(LINE + 1)));
+ assertDuplications(FILE_2_REF, singleLineDetailedTextBlock(1, LINE), new InnerDuplicate(singleLineTextBlock(LINE + 1)));
}
@Test
underTest.execute();
- assertDuplications(FILE_1_REF, singleLineTextBlock(LINE), new InProjectDuplicate(treeRootHolder.getComponentByRef(FILE_2_REF), singleLineTextBlock(LINE + 1)));
+ assertDuplications(FILE_1_REF, singleLineDetailedTextBlock(1, LINE), new InProjectDuplicate(treeRootHolder.getComponentByRef(FILE_2_REF), singleLineTextBlock(LINE + 1)));
assertNoDuplication(FILE_2_REF);
}
Component file1Component = treeRootHolder.getComponentByRef(FILE_1_REF);
assertThat(duplicationRepository.getDuplications(FILE_2_REF)).containsOnly(
duplication(
- singleLineTextBlock(LINE),
+ singleLineDetailedTextBlock(1, LINE),
new InnerDuplicate(singleLineTextBlock(LINE + 1)), new InnerDuplicate(singleLineTextBlock(LINE + 2)), new InProjectDuplicate(file1Component, singleLineTextBlock(LINE)),
new InProjectDuplicate(file1Component, singleLineTextBlock(LINE + 10))),
duplication(
- singleLineTextBlock(OTHER_LINE),
+ singleLineDetailedTextBlock(2, OTHER_LINE),
new InProjectDuplicate(file1Component, singleLineTextBlock(OTHER_LINE))
),
duplication(
- singleLineTextBlock(OTHER_LINE + 80),
+ singleLineDetailedTextBlock(3, OTHER_LINE + 80),
new InnerDuplicate(singleLineTextBlock(LINE)), new InnerDuplicate(singleLineTextBlock(LINE + 10))
)
);
}
+ @Test
+ public void loads_never_consider_originals_from_batch_on_same_lines_as_the_equals() {
+ reportReader.putDuplications(
+ FILE_2_REF,
+ createDuplication(
+ singleLineTextRange(LINE),
+ createInnerDuplicate(LINE + 1), createInnerDuplicate(LINE + 2), createInProjectDuplicate(FILE_1_REF, LINE + 2)),
+ createDuplication(
+ singleLineTextRange(LINE),
+ createInnerDuplicate(LINE + 2), createInnerDuplicate(LINE + 3), createInProjectDuplicate(FILE_1_REF, LINE + 2))
+ );
+
+ underTest.execute();
+
+ Component file1Component = treeRootHolder.getComponentByRef(FILE_1_REF);
+ assertThat(duplicationRepository.getDuplications(FILE_2_REF)).containsOnly(
+ duplication(
+ singleLineDetailedTextBlock(1, LINE),
+ new InnerDuplicate(singleLineTextBlock(LINE + 1)), new InnerDuplicate(singleLineTextBlock(LINE + 2)),
+ new InProjectDuplicate(file1Component, singleLineTextBlock(LINE + 2))
+ ),
+ duplication(
+ singleLineDetailedTextBlock(2, LINE),
+ new InnerDuplicate(singleLineTextBlock(LINE + 2)), new InnerDuplicate(singleLineTextBlock(LINE + 3)),
+ new InProjectDuplicate(file1Component, singleLineTextBlock(LINE + 2))
+ )
+ );
+ }
+
@Test
public void loads_duplication_with_otherFileRef_throws_IAE_if_component_does_not_exist() {
int line = 2;
return new TextBlock(line, line);
}
+ private DetailedTextBlock singleLineDetailedTextBlock(int id, int line) {
+ return new DetailedTextBlock(id, line, line);
+ }
+
private static BatchReport.Duplication createDuplication(BatchReport.TextRange original, BatchReport.Duplicate... duplicates) {
BatchReport.Duplication.Builder builder = BatchReport.Duplication.newBuilder()
.setOriginPosition(original);