aboutsummaryrefslogtreecommitdiffstats
path: root/catalog-dao/src/main/java/org/openecomp/sdc/be/dao/utils/MapUtil.java
blob: 5b0ad9cc249acbf6317348b54107e0e05ab3ebca (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
/*-
 * ============LICENSE_START=======================================================
 * SDC
 * ================================================================================
 * Copyright (C) 2017 AT&T Intellectual Property. All rights reserved.
 * ================================================================================
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 * ============LICENSE_END=========================================================
 */

package org.openecomp.sdc.be.dao.utils;

import java.util.*;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

import static java.util.stream.Collectors.toList;
/**
 * Utility class to ease map manipulation.
 */
public final class MapUtil {
    private MapUtil() {
    }

    /**
     * Try to get a value following a path in the map. For example :
     * MapUtil.get(map, "a.b.c") correspond to: map.get(a).get(b).get(c)
     *
     * @param map  the map to search for path
     * @param path keys in the map separated by '.'
     */
    public static Object get(Map<String, ? extends Object> map, String path) {
        String[] tokens = path.split("\\.");
        if (tokens.length == 0) {
            return null;
        } else {
            Object value = map;
            for (String token : tokens) {
                if (!(value instanceof Map)) {
                    return null;
                } else {
                    @SuppressWarnings("unchecked")
                    Map<String, Object> nested = (Map<String, Object>) value;
                    if (nested.containsKey(token)) {
                        value = nested.get(token);
                    } else {
                        return null;
                    }
                }
            }
            return value;
        }
    }

    /**
     * @param valuesToMap      the list of values to group
     * @param groupingFunction the function to group the list values by
     * @return a map of list of values grouped by a key, as specified in the {@code groupingFunction}
     */
    public static <K, V> Map<K, List<V>> groupListBy(Collection<V> valuesToMap, Function<V, K> groupingFunction) {
        return valuesToMap.stream().collect(Collectors.groupingBy(groupingFunction));
    }

    /**
     * @param valuesToMap     list of values to map
     * @param mappingFunction a function which specifies how to map each element on the list
     * @return a map created by mapping each element from the {@code valuesToMap} as specified in the {@code mappingFunction}
     */
    public static <K, V> Map<K, V> toMap(Collection<V> valuesToMap, Function<V, K> mappingFunction) {
        return toMap(valuesToMap, mappingFunction, throwingMerger());
    }
    
    public static <K, V> Map<K, V> toMap(Collection<V> valuesToMap, Function<V, K> mappingFunction, BinaryOperator<V> mergeFunction) {
        return streamOfNullable(valuesToMap).collect(Collectors.toMap(mappingFunction, Function.identity(), mergeFunction));
    }


    /**
     * merge two maps. if a key exists in both maps, takes the value from {@code first}
     *
     * @param first  the first map to merge
     * @param second the second map to merge
     * @return the merged map
     */
    public static <K, V> Map<K, V> mergeMaps(Map<K, V> first, Map<K, V> second) {
        if (first == null && second == null) {
            return new HashMap<>();
        }
        if (first != null && second == null) {
            return new HashMap<>(first);
        }
        if (first == null) {
            return new HashMap<>(second);
        }
        Map<K, V> mergedMap = new HashMap<>(first);
        second.forEach(mergedMap::putIfAbsent);
        return mergedMap;
    }

    public static <K, V> List<V> flattenMapValues(Map<K, List<V>> mapToFlatten) {
        if (mapToFlatten == null) {
            return new ArrayList<>();
        }
        return mapToFlatten.values().stream().flatMap(Collection::stream).collect(toList());
    }

    /**
     * @param map                the map of which it keys to convert
     * @param keyMappingFunction a function which converts the key object
     * @return a map with converted keys.
     */
    public static <K, U, V> Map<U, List<V>> convertMapKeys(Map<K, List<V>> map, Function<K, U> keyMappingFunction) {
        return map.entrySet().stream()
                .collect(Collectors.toMap(entry -> keyMappingFunction.apply(entry.getKey()),
                        Map.Entry::getValue));
    }

    /**
     * Create a new hash map and fills it from the given keys and values
     * (keys[index] -> values[index].
     *
     * @param keys   The array of keys.
     * @param values The array of values.
     * @return A map that contains for each key element in the keys array a
     * value from the values array at the same index.
     */
    public static <K, V> Map<K, V> newHashMap(K[] keys, V[] values) {
        Map<K, V> map = new HashMap<>();
        if (keys == null || values == null || keys.length != values.length) {
            throw new IllegalArgumentException("keys and values must be non-null and have the same size.");
        }
        for (int i = 0; i < keys.length; i++) {
            map.put(keys[i], values[i]);
        }
        return map;
    }
    
    
    /**
     * Returns a merge function, suitable for use in
     * {@link Map#merge(Object, Object, BiFunction) Map.merge()} or
     * {@link #toMap(Function, Function, BinaryOperator) toMap()}, which always
     * throws {@code IllegalStateException}.  This can be used to enforce the
     * assumption that the elements being collected are distinct.
     *
     * @param <T> the type of input arguments to the merge function
     * @return a merge function which always throw {@code IllegalStateException}
     */
    private static <T> BinaryOperator<T> throwingMerger() {
        return (u,v) -> { throw new IllegalStateException(String.format("Duplicate key %s", u)); };
    }

    public static <V> Stream<V> streamOfNullable(Collection<V> collection) {
        return collection == null? Stream.empty(): collection.stream();
    }
    
    public static<T> Stream<T> streamOfNullable(T t) {
        return t == null ? Stream.empty() : Stream.of(t);
    }
}