]> source.dussan.org Git - sonarqube.git/blob
c858b2d68fcdaf23402ae24926497b891b0c6099
[sonarqube.git] /
1 /*
2  * SonarQube
3  * Copyright (C) 2009-2019 SonarSource SA
4  * mailto:info AT sonarsource DOT com
5  *
6  * This program is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 3 of the License, or (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public License
17  * along with this program; if not, write to the Free Software Foundation,
18  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
19  */
20 package org.sonar.ce.task.projectanalysis.duplication;
21
22 import com.google.common.base.Function;
23 import com.google.common.base.Predicate;
24 import com.google.common.collect.Ordering;
25 import java.util.Comparator;
26 import java.util.Objects;
27 import java.util.SortedSet;
28 import javax.annotation.Nonnull;
29 import javax.annotation.Nullable;
30 import javax.annotation.concurrent.Immutable;
31
32 import static com.google.common.base.Preconditions.checkArgument;
33 import static com.google.common.collect.FluentIterable.from;
34 import static java.util.Objects.requireNonNull;
35
36 @Immutable
37 public final class Duplication {
38   private static final Ordering<Duplicate> DUPLICATE_ORDERING = Ordering.from(DuplicateComparatorByType.INSTANCE)
39     .compound(Ordering.natural().onResultOf(DuplicateToFileKey.INSTANCE))
40     .compound(Ordering.natural().onResultOf(DuplicateToTextBlock.INSTANCE));
41
42   private final TextBlock original;
43   private final SortedSet<Duplicate> duplicates;
44
45   /**
46    * @throws NullPointerException if {@code original} is {@code null} or {@code duplicates} is {@code null} or {@code duplicates} contains {@code null}
47    * @throws IllegalArgumentException if {@code duplicates} is empty
48    * @throws IllegalArgumentException if {@code duplicates} contains a {@link InnerDuplicate} with {@code original}
49    */
50   public Duplication(final TextBlock original, final Iterable<Duplicate> duplicates) {
51     this.original = requireNonNull(original, "original TextBlock can not be null");
52     this.duplicates = from(requireNonNull(duplicates, "duplicates can not be null"))
53       .filter(FailOnNullDuplicate.INSTANCE)
54       .filter(new EnsureInnerDuplicateIsNotOriginalTextBlock(original))
55       .toSortedSet(DUPLICATE_ORDERING);
56     checkArgument(!this.duplicates.isEmpty(), "duplicates can not be empty");
57   }
58
59   /**
60    * The duplicated block.
61    */
62   public TextBlock getOriginal() {
63     return this.original;
64   }
65
66   /**
67    * The duplicates of the original, sorted by inner duplicates, then project duplicates, then cross-project duplicates.
68    * For each category of duplicate, they are sorted by:
69    * <ul>
70    *   <li>file key (unless it's an InnerDuplicate)</li>
71    *   <li>then by TextBlocks by start line and in case of same line, by shortest first</li>
72    * </ul
73    * <p>The returned set can not be empty and no inner duplicate can contain the original {@link TextBlock}.</p>
74    */
75   public SortedSet<Duplicate> getDuplicates() {
76     return this.duplicates;
77   }
78
79   @Override
80   public boolean equals(@Nullable Object o) {
81     if (this == o) {
82       return true;
83     }
84     if (o == null || getClass() != o.getClass()) {
85       return false;
86     }
87     Duplication that = (Duplication) o;
88     return original.equals(that.original) && duplicates.equals(that.duplicates);
89   }
90
91   @Override
92   public int hashCode() {
93     return Objects.hash(original, duplicates);
94   }
95
96   @Override
97   public String toString() {
98     return "Duplication{" +
99       "original=" + original +
100       ", duplicates=" + duplicates +
101       '}';
102   }
103
104   private enum FailOnNullDuplicate implements Predicate<Duplicate> {
105     INSTANCE;
106
107     @Override
108     public boolean apply(@Nullable Duplicate input) {
109       requireNonNull(input, "duplicates can not contain null");
110       return true;
111     }
112   }
113
114   private enum DuplicateComparatorByType implements Comparator<Duplicate> {
115     INSTANCE;
116
117     @Override
118     public int compare(Duplicate o1, Duplicate o2) {
119       return toIndexType(o1) - toIndexType(o2);
120     }
121
122     private static int toIndexType(Duplicate duplicate) {
123       if (duplicate instanceof InnerDuplicate) {
124         return 0;
125       }
126       if (duplicate instanceof InProjectDuplicate) {
127         return 1;
128       }
129       if (duplicate instanceof CrossProjectDuplicate) {
130         return 2;
131       }
132       throw new IllegalArgumentException("Unsupported type of Duplicate " + duplicate.getClass().getName());
133     }
134   }
135
136   private enum DuplicateToTextBlock implements Function<Duplicate, TextBlock> {
137     INSTANCE;
138
139     @Override
140     @Nonnull
141     public TextBlock apply(@Nonnull Duplicate input) {
142       return input.getTextBlock();
143     }
144   }
145
146   private static class EnsureInnerDuplicateIsNotOriginalTextBlock implements Predicate<Duplicate> {
147     private final TextBlock original;
148
149     public EnsureInnerDuplicateIsNotOriginalTextBlock(TextBlock original) {
150       this.original = original;
151     }
152
153     @Override
154     public boolean apply(@Nullable Duplicate input) {
155       if (input instanceof InnerDuplicate) {
156         checkArgument(!original.equals(input.getTextBlock()), "TextBlock of an InnerDuplicate can not be the original TextBlock");
157       }
158       return true;
159     }
160   }
161
162   private enum DuplicateToFileKey implements Function<Duplicate, String> {
163     INSTANCE;
164
165     @Override
166     @Nonnull
167     public String apply(@Nonnull Duplicate duplicate) {
168       if (duplicate instanceof InnerDuplicate) {
169         return "";
170       }
171       if (duplicate instanceof InProjectDuplicate) {
172         return ((InProjectDuplicate) duplicate).getFile().getDbKey();
173       }
174       if (duplicate instanceof CrossProjectDuplicate) {
175         return ((CrossProjectDuplicate) duplicate).getFileKey();
176       }
177       throw new IllegalArgumentException("Unsupported type of Duplicate " + duplicate.getClass().getName());
178     }
179   }
180 }