aboutsummaryrefslogtreecommitdiffstats
path: root/sonar-core
diff options
context:
space:
mode:
authorSébastien Lesaint <sebastien.lesaint@sonarsource.com>2019-04-16 15:19:27 +0200
committersonartech <sonartech@sonarsource.com>2019-04-23 10:37:57 +0200
commit58bb4b37da6e32a113870b0fc98d5494379641b6 (patch)
tree027172a0a7945ea6b2f3dae5d917e1fce1a66c81 /sonar-core
parentd9e7cb020409491b45199ab8762eb22746e3543d (diff)
downloadsonarqube-58bb4b37da6e32a113870b0fc98d5494379641b6.tar.gz
sonarqube-58bb4b37da6e32a113870b0fc98d5494379641b6.zip
SONAR-11757 single notification for FPs and changes on my issues
Diffstat (limited to 'sonar-core')
-rw-r--r--sonar-core/src/main/java/org/sonar/core/util/stream/MoreCollectors.java107
-rw-r--r--sonar-core/src/test/java/org/sonar/core/util/stream/MoreCollectorsTest.java247
2 files changed, 332 insertions, 22 deletions
diff --git a/sonar-core/src/main/java/org/sonar/core/util/stream/MoreCollectors.java b/sonar-core/src/main/java/org/sonar/core/util/stream/MoreCollectors.java
index 9da92dc857c..3fac4dd4df2 100644
--- a/sonar-core/src/main/java/org/sonar/core/util/stream/MoreCollectors.java
+++ b/sonar-core/src/main/java/org/sonar/core/util/stream/MoreCollectors.java
@@ -24,6 +24,7 @@ import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableListMultimap;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSet;
+import com.google.common.collect.ImmutableSetMultimap;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.HashMap;
@@ -37,12 +38,15 @@ import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import java.util.stream.Collectors;
+import java.util.stream.Stream;
import static java.util.Objects.requireNonNull;
public final class MoreCollectors {
private static final int DEFAULT_HASHMAP_CAPACITY = 0;
+ private static final String KEY_FUNCTION_CANT_RETURN_NULL_MESSAGE = "Key function can't return null";
+ private static final String VALUE_FUNCTION_CANT_RETURN_NULL_MESSAGE = "Value function can't return null";
private MoreCollectors() {
// prevents instantiation
@@ -247,11 +251,11 @@ public final class MoreCollectors {
*/
public static <K, E, V> Collector<E, Map<K, V>, ImmutableMap<K, V>> uniqueIndex(Function<? super E, K> keyFunction,
Function<? super E, V> valueFunction, int expectedSize) {
- requireNonNull(keyFunction, "Key function can't be null");
- requireNonNull(valueFunction, "Value function can't be null");
+ verifyKeyAndValueFunctions(keyFunction, valueFunction);
+
BiConsumer<Map<K, V>, E> accumulator = (map, element) -> {
- K key = requireNonNull(keyFunction.apply(element), "Key function can't return null");
- V value = requireNonNull(valueFunction.apply(element), "Value function can't return null");
+ K key = requireNonNull(keyFunction.apply(element), KEY_FUNCTION_CANT_RETURN_NULL_MESSAGE);
+ V value = requireNonNull(valueFunction.apply(element), VALUE_FUNCTION_CANT_RETURN_NULL_MESSAGE);
putAndFailOnDuplicateKey(map, key, value);
};
@@ -328,11 +332,11 @@ public final class MoreCollectors {
*/
public static <K, E, V> Collector<E, ImmutableListMultimap.Builder<K, V>, ImmutableListMultimap<K, V>> index(Function<? super E, K> keyFunction,
Function<? super E, V> valueFunction) {
- requireNonNull(keyFunction, "Key function can't be null");
- requireNonNull(valueFunction, "Value function can't be null");
+ verifyKeyAndValueFunctions(keyFunction, valueFunction);
+
BiConsumer<ImmutableListMultimap.Builder<K, V>, E> accumulator = (map, element) -> {
- K key = requireNonNull(keyFunction.apply(element), "Key function can't return null");
- V value = requireNonNull(valueFunction.apply(element), "Value function can't return null");
+ K key = requireNonNull(keyFunction.apply(element), KEY_FUNCTION_CANT_RETURN_NULL_MESSAGE);
+ V value = requireNonNull(valueFunction.apply(element), VALUE_FUNCTION_CANT_RETURN_NULL_MESSAGE);
map.put(key, value);
};
@@ -350,6 +354,93 @@ public final class MoreCollectors {
}
/**
+ * Creates an {@link com.google.common.collect.ImmutableSetMultimap} from the stream where the values are the values
+ * in the stream and the keys are the result of the provided {@link Function keyFunction} applied to each value in the
+ * stream.
+ *
+ * <p>
+ * Neither {@link Function keyFunction} nor {@link Function valueFunction} can return {@code null}, otherwise a
+ * {@link NullPointerException} will be thrown.
+ * </p>
+ *
+ * @throws NullPointerException if {@code keyFunction} or {@code valueFunction} is {@code null}.
+ * @throws NullPointerException if result of {@code keyFunction} or {@code valueFunction} is {@code null}.
+ */
+ public static <K, E> Collector<E, ImmutableSetMultimap.Builder<K, E>, ImmutableSetMultimap<K, E>> unorderedIndex(Function<? super E, K> keyFunction) {
+ return unorderedIndex(keyFunction, Function.identity());
+ }
+
+ /**
+ * Creates an {@link com.google.common.collect.ImmutableSetMultimap} from the stream where the values are the result
+ * of {@link Function valueFunction} applied to the values in the stream and the keys are the result of the provided
+ * {@link Function keyFunction} applied to each value in the stream.
+ *
+ * <p>
+ * Neither {@link Function keyFunction} nor {@link Function valueFunction} can return {@code null}, otherwise a
+ * {@link NullPointerException} will be thrown.
+ * </p>
+ *
+ * @throws NullPointerException if {@code keyFunction} or {@code valueFunction} is {@code null}.
+ * @throws NullPointerException if result of {@code keyFunction} or {@code valueFunction} is {@code null}.
+ */
+ public static <K, E, V> Collector<E, ImmutableSetMultimap.Builder<K, V>, ImmutableSetMultimap<K, V>> unorderedIndex(Function<? super E, K> keyFunction,
+ Function<? super E, V> valueFunction) {
+ verifyKeyAndValueFunctions(keyFunction, valueFunction);
+
+ BiConsumer<ImmutableSetMultimap.Builder<K, V>, E> accumulator = (map, element) -> {
+ K key = requireNonNull(keyFunction.apply(element), KEY_FUNCTION_CANT_RETURN_NULL_MESSAGE);
+ V value = requireNonNull(valueFunction.apply(element), VALUE_FUNCTION_CANT_RETURN_NULL_MESSAGE);
+
+ map.put(key, value);
+ };
+ BinaryOperator<ImmutableSetMultimap.Builder<K, V>> merger = (m1, m2) -> {
+ for (Map.Entry<K, V> entry : m2.build().entries()) {
+ m1.put(entry.getKey(), entry.getValue());
+ }
+ return m1;
+ };
+ return Collector.of(
+ ImmutableSetMultimap::builder,
+ accumulator,
+ merger,
+ ImmutableSetMultimap.Builder::build);
+ }
+
+ /**
+ * A Collector similar to {@link #unorderedIndex(Function, Function)} except that it expects the {@code valueFunction}
+ * to return a {@link Stream} which content will be flatten into the returned {@link ImmutableSetMultimap}.
+ *
+ * @see #unorderedIndex(Function, Function)
+ */
+ public static <K, E, V> Collector<E, ImmutableSetMultimap.Builder<K, V>, ImmutableSetMultimap<K, V>> unorderedFlattenIndex(
+ Function<? super E, K> keyFunction, Function<? super E, Stream<V>> valueFunction) {
+ verifyKeyAndValueFunctions(keyFunction, valueFunction);
+
+ BiConsumer<ImmutableSetMultimap.Builder<K, V>, E> accumulator = (map, element) -> {
+ K key = requireNonNull(keyFunction.apply(element), KEY_FUNCTION_CANT_RETURN_NULL_MESSAGE);
+ Stream<V> valueStream = requireNonNull(valueFunction.apply(element), VALUE_FUNCTION_CANT_RETURN_NULL_MESSAGE);
+
+ valueStream.forEach(value -> map.put(key, value));
+ };
+ BinaryOperator<ImmutableSetMultimap.Builder<K, V>> merger = (m1, m2) -> {
+ for (Map.Entry<K, V> entry : m2.build().entries()) {
+ m1.put(entry.getKey(), entry.getValue());
+ }
+ return m1;
+ };
+ return Collector.of(
+ ImmutableSetMultimap::builder,
+ accumulator,
+ merger,
+ ImmutableSetMultimap.Builder::build);
+ }
+
+ private static void verifyKeyAndValueFunctions(Function<?, ?> keyFunction, Function<?, ?> valueFunction) {
+ requireNonNull(keyFunction, "Key function can't be null");
+ requireNonNull(valueFunction, "Value function can't be null");
+ }
+
+ /**
* Applies the specified {@link Joiner} to the current stream.
*
* @throws NullPointerException of {@code joiner} is {@code null}
diff --git a/sonar-core/src/test/java/org/sonar/core/util/stream/MoreCollectorsTest.java b/sonar-core/src/test/java/org/sonar/core/util/stream/MoreCollectorsTest.java
index 1de9bca52fd..713c33f217a 100644
--- a/sonar-core/src/test/java/org/sonar/core/util/stream/MoreCollectorsTest.java
+++ b/sonar-core/src/test/java/org/sonar/core/util/stream/MoreCollectorsTest.java
@@ -22,7 +22,8 @@ package org.sonar.core.util.stream;
import com.google.common.base.Joiner;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
-import com.google.common.collect.Multimap;
+import com.google.common.collect.ListMultimap;
+import com.google.common.collect.SetMultimap;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -32,6 +33,7 @@ import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
+import java.util.stream.Collectors;
import java.util.stream.IntStream;
import java.util.stream.Stream;
import org.junit.Rule;
@@ -48,6 +50,8 @@ import static org.sonar.core.util.stream.MoreCollectors.toHashSet;
import static org.sonar.core.util.stream.MoreCollectors.toList;
import static org.sonar.core.util.stream.MoreCollectors.toSet;
import static org.sonar.core.util.stream.MoreCollectors.uniqueIndex;
+import static org.sonar.core.util.stream.MoreCollectors.unorderedFlattenIndex;
+import static org.sonar.core.util.stream.MoreCollectors.unorderedIndex;
public class MoreCollectorsTest {
@@ -57,9 +61,16 @@ public class MoreCollectorsTest {
private static final MyObj MY_OBJ_1_C = new MyObj(1, "C");
private static final MyObj MY_OBJ_2_B = new MyObj(2, "B");
private static final MyObj MY_OBJ_3_C = new MyObj(3, "C");
+ private static final MyObj2 MY_OBJ2_1_A_X = new MyObj2(1, "A", "X");
+ private static final MyObj2 MY_OBJ2_1_C = new MyObj2(1, "C");
+ private static final MyObj2 MY_OBJ2_2_B = new MyObj2(2, "B");
+ private static final MyObj2 MY_OBJ2_3_C = new MyObj2(3, "C");
private static final List<MyObj> SINGLE_ELEMENT_LIST = Arrays.asList(MY_OBJ_1_A);
+ private static final List<MyObj2> SINGLE_ELEMENT2_LIST = Arrays.asList(MY_OBJ2_1_A_X);
private static final List<MyObj> LIST_WITH_DUPLICATE_ID = Arrays.asList(MY_OBJ_1_A, MY_OBJ_2_B, MY_OBJ_1_C);
+ private static final List<MyObj2> LIST2_WITH_DUPLICATE_ID = Arrays.asList(MY_OBJ2_1_A_X, MY_OBJ2_2_B, MY_OBJ2_1_C);
private static final List<MyObj> LIST = Arrays.asList(MY_OBJ_1_A, MY_OBJ_2_B, MY_OBJ_3_C);
+ private static final List<MyObj2> LIST2 = Arrays.asList(MY_OBJ2_1_A_X, MY_OBJ2_2_B, MY_OBJ2_3_C);
@Rule
public ExpectedException expectedException = ExpectedException.none();
@@ -357,6 +368,15 @@ public class MoreCollectorsTest {
}
@Test
+ public void uniqueIndex_supports_duplicate_keys() {
+ ListMultimap<Integer, String> multimap = LIST_WITH_DUPLICATE_ID.stream().collect(index(MyObj::getId, MyObj::getText));
+
+ assertThat(multimap.keySet()).containsOnly(1, 2);
+ assertThat(multimap.get(1)).containsOnly("A", "C");
+ assertThat(multimap.get(2)).containsOnly("B");
+ }
+
+ @Test
public void index_empty_stream_returns_empty_map() {
assertThat(Collections.<MyObj>emptyList().stream().collect(index(MyObj::getId)).size()).isEqualTo(0);
assertThat(Collections.<MyObj>emptyList().stream().collect(index(MyObj::getId, MyObj::getText)).size()).isEqualTo(0);
@@ -409,7 +429,7 @@ public class MoreCollectorsTest {
@Test
public void index_supports_duplicate_keys() {
- Multimap<Integer, MyObj> multimap = LIST_WITH_DUPLICATE_ID.stream().collect(index(MyObj::getId));
+ ListMultimap<Integer, MyObj> multimap = LIST_WITH_DUPLICATE_ID.stream().collect(index(MyObj::getId));
assertThat(multimap.keySet()).containsOnly(1, 2);
assertThat(multimap.get(1)).containsOnly(MY_OBJ_1_A, MY_OBJ_1_C);
@@ -417,17 +437,104 @@ public class MoreCollectorsTest {
}
@Test
- public void uniqueIndex_supports_duplicate_keys() {
- Multimap<Integer, String> multimap = LIST_WITH_DUPLICATE_ID.stream().collect(index(MyObj::getId, MyObj::getText));
+ public void index_returns_ListMultimap() {
+ ListMultimap<Integer, MyObj> multimap = LIST.stream().collect(index(MyObj::getId));
+
+ assertThat(multimap.size()).isEqualTo(3);
+ Map<Integer, Collection<MyObj>> map = multimap.asMap();
+ assertThat(map.get(1)).containsOnly(MY_OBJ_1_A);
+ assertThat(map.get(2)).containsOnly(MY_OBJ_2_B);
+ assertThat(map.get(3)).containsOnly(MY_OBJ_3_C);
+ }
+
+ @Test
+ public void index_with_valueFunction_returns_ListMultimap() {
+ ListMultimap<Integer, String> multimap = LIST.stream().collect(index(MyObj::getId, MyObj::getText));
+
+ assertThat(multimap.size()).isEqualTo(3);
+ Map<Integer, Collection<String>> map = multimap.asMap();
+ assertThat(map.get(1)).containsOnly("A");
+ assertThat(map.get(2)).containsOnly("B");
+ assertThat(map.get(3)).containsOnly("C");
+ }
+
+ @Test
+ public void index_parallel_stream() {
+ ListMultimap<String, String> multimap = HUGE_LIST.parallelStream().collect(index(identity()));
+
+ assertThat(multimap.keySet()).isEqualTo(HUGE_SET);
+ }
+
+ @Test
+ public void index_with_valueFunction_parallel_stream() {
+ ListMultimap<String, String> multimap = HUGE_LIST.parallelStream().collect(index(identity(), identity()));
+
+ assertThat(multimap.keySet()).isEqualTo(HUGE_SET);
+ }
+
+ @Test
+ public void unorderedIndex_empty_stream_returns_empty_map() {
+ assertThat(Collections.<MyObj>emptyList().stream().collect(unorderedIndex(MyObj::getId)).size()).isEqualTo(0);
+ assertThat(Collections.<MyObj>emptyList().stream().collect(unorderedIndex(MyObj::getId, MyObj::getText)).size()).isEqualTo(0);
+ }
+
+ @Test
+ public void unorderedIndex_fails_if_key_function_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("Key function can't be null");
+
+ unorderedIndex(null);
+ }
+
+ @Test
+ public void unorderedIndex_with_valueFunction_fails_if_key_function_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("Key function can't be null");
+
+ unorderedIndex(null, MyObj::getText);
+ }
+
+ @Test
+ public void unorderedIndex_with_valueFunction_fails_if_value_function_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("Value function can't be null");
+
+ unorderedIndex(MyObj::getId, null);
+ }
+
+ @Test
+ public void unorderedIndex_fails_if_key_function_returns_null() {
+ expectKeyFunctionCantReturnNullNPE();
+
+ SINGLE_ELEMENT_LIST.stream().collect(unorderedIndex(s -> null));
+ }
+
+ @Test
+ public void unorderedIndex_with_valueFunction_fails_if_key_function_returns_null() {
+ expectKeyFunctionCantReturnNullNPE();
+
+ SINGLE_ELEMENT_LIST.stream().collect(unorderedIndex(s -> null, MyObj::getText));
+ }
+
+ @Test
+ public void unorderedIndex_with_valueFunction_fails_if_value_function_returns_null() {
+ expectValueFunctionCantReturnNullNPE();
+
+ SINGLE_ELEMENT_LIST.stream().collect(unorderedIndex(MyObj::getId, s -> null));
+ }
+
+ @Test
+ public void unorderedIndex_supports_duplicate_keys() {
+ SetMultimap<Integer, MyObj> multimap = LIST_WITH_DUPLICATE_ID.stream().collect(unorderedIndex(MyObj::getId));
assertThat(multimap.keySet()).containsOnly(1, 2);
- assertThat(multimap.get(1)).containsOnly("A", "C");
- assertThat(multimap.get(2)).containsOnly("B");
+ assertThat(multimap.get(1)).containsOnly(MY_OBJ_1_A, MY_OBJ_1_C);
+ assertThat(multimap.get(2)).containsOnly(MY_OBJ_2_B);
}
@Test
- public void index_returns_multimap() {
- Multimap<Integer, MyObj> multimap = LIST.stream().collect(index(MyObj::getId));
+ public void unorderedIndex_returns_SetMultimap() {
+ SetMultimap<Integer, MyObj> multimap = LIST.stream().collect(unorderedIndex(MyObj::getId));
assertThat(multimap.size()).isEqualTo(3);
Map<Integer, Collection<MyObj>> map = multimap.asMap();
@@ -437,8 +544,8 @@ public class MoreCollectorsTest {
}
@Test
- public void index_with_valueFunction_returns_multimap() {
- Multimap<Integer, String> multimap = LIST.stream().collect(index(MyObj::getId, MyObj::getText));
+ public void unorderedIndex_with_valueFunction_returns_SetMultimap() {
+ SetMultimap<Integer, String> multimap = LIST.stream().collect(unorderedIndex(MyObj::getId, MyObj::getText));
assertThat(multimap.size()).isEqualTo(3);
Map<Integer, Collection<String>> map = multimap.asMap();
@@ -448,19 +555,113 @@ public class MoreCollectorsTest {
}
@Test
- public void index_parallel_stream() {
- Multimap<String, String> multimap = HUGE_LIST.parallelStream().collect(index(identity()));
+ public void unorderedIndex_parallel_stream() {
+ SetMultimap<String, String> multimap = HUGE_LIST.parallelStream().collect(unorderedIndex(identity()));
assertThat(multimap.keySet()).isEqualTo(HUGE_SET);
}
@Test
- public void index_with_valueFunction_parallel_stream() {
- Multimap<String, String> multimap = HUGE_LIST.parallelStream().collect(index(identity(), identity()));
+ public void unorderedIndex_with_valueFunction_parallel_stream() {
+ SetMultimap<String, String> multimap = HUGE_LIST.parallelStream().collect(unorderedIndex(identity(), identity()));
+
+ assertThat(multimap.keySet()).isEqualTo(HUGE_SET);
+ }
+
+
+
+
+
+
+ @Test
+ public void unorderedFlattenIndex_empty_stream_returns_empty_map() {
+ assertThat(Collections.<MyObj2>emptyList().stream()
+ .collect(unorderedFlattenIndex(MyObj2::getId, MyObj2::getTexts))
+ .size()).isEqualTo(0);
+ }
+
+ @Test
+ public void unorderedFlattenIndex_with_valueFunction_fails_if_key_function_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("Key function can't be null");
+
+ unorderedFlattenIndex(null, MyObj2::getTexts);
+ }
+
+ @Test
+ public void unorderedFlattenIndex_with_valueFunction_fails_if_value_function_is_null() {
+ expectedException.expect(NullPointerException.class);
+ expectedException.expectMessage("Value function can't be null");
+
+ unorderedFlattenIndex(MyObj2::getId, null);
+ }
+
+ @Test
+ public void unorderedFlattenIndex_with_valueFunction_fails_if_key_function_returns_null() {
+ expectKeyFunctionCantReturnNullNPE();
+
+ SINGLE_ELEMENT2_LIST.stream().collect(unorderedFlattenIndex(s -> null, MyObj2::getTexts));
+ }
+
+ @Test
+ public void unorderedFlattenIndex_with_valueFunction_fails_if_value_function_returns_null() {
+ expectValueFunctionCantReturnNullNPE();
+
+ SINGLE_ELEMENT2_LIST.stream().collect(unorderedFlattenIndex(MyObj2::getId, s -> null));
+ }
+
+ @Test
+ public void unorderedFlattenIndex_supports_duplicate_keys() {
+ SetMultimap<Integer, String> multimap = LIST2_WITH_DUPLICATE_ID.stream()
+ .collect(unorderedFlattenIndex(MyObj2::getId, MyObj2::getTexts));
+
+ assertThat(multimap.keySet()).containsOnly(1, 2);
+ assertThat(multimap.get(1)).containsOnly("A", "X", "C");
+ assertThat(multimap.get(2)).containsOnly("B");
+ }
+
+ @Test
+ public void unorderedFlattenIndex_with_valueFunction_returns_SetMultimap() {
+ SetMultimap<Integer, String> multimap = LIST2.stream()
+ .collect(unorderedFlattenIndex(MyObj2::getId, MyObj2::getTexts));
+
+ assertThat(multimap.size()).isEqualTo(4);
+ Map<Integer, Collection<String>> map = multimap.asMap();
+ assertThat(map.get(1)).containsOnly("A", "X");
+ assertThat(map.get(2)).containsOnly("B");
+ assertThat(map.get(3)).containsOnly("C");
+ }
+
+ @Test
+ public void unorderedFlattenIndex_with_valueFunction_parallel_stream() {
+ SetMultimap<String, String> multimap = HUGE_LIST.parallelStream().collect(unorderedFlattenIndex(identity(), Stream::of));
assertThat(multimap.keySet()).isEqualTo(HUGE_SET);
}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@Test
public void join_on_empty_stream_returns_empty_string() {
assertThat(Collections.emptyList().stream().collect(join(Joiner.on(",")))).isEmpty();
@@ -532,6 +733,24 @@ public class MoreCollectorsTest {
}
}
+ private static final class MyObj2 {
+ private final int id;
+ private final List<String> texts;
+
+ public MyObj2(int id, String... texts) {
+ this.id = id;
+ this.texts = Arrays.stream(texts).collect(Collectors.toList());
+ }
+
+ public int getId() {
+ return id;
+ }
+
+ public Stream<String> getTexts() {
+ return texts.stream();
+ }
+ }
+
private enum MyEnum {
ONE, TWO, THREE
}