diff options
author | Sébastien Lesaint <sebastien.lesaint@sonarsource.com> | 2019-04-16 15:19:27 +0200 |
---|---|---|
committer | sonartech <sonartech@sonarsource.com> | 2019-04-23 10:37:57 +0200 |
commit | 58bb4b37da6e32a113870b0fc98d5494379641b6 (patch) | |
tree | 027172a0a7945ea6b2f3dae5d917e1fce1a66c81 /sonar-core | |
parent | d9e7cb020409491b45199ab8762eb22746e3543d (diff) | |
download | sonarqube-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.java | 107 | ||||
-rw-r--r-- | sonar-core/src/test/java/org/sonar/core/util/stream/MoreCollectorsTest.java | 247 |
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 } |