BUG-4803: introduce unordered offset maps
[yangtools.git] / common / util / src / main / java / org / opendaylight / yangtools / util / OffsetMapCache.java
1 /*
2  * Copyright (c) 2015 Cisco Systems, Inc. and others.  All rights reserved.
3  *
4  * This program and the accompanying materials are made available under the
5  * terms of the Eclipse Public License v1.0 which accompanies this distribution,
6  * and is available at http://www.eclipse.org/legal/epl-v10.html
7  */
8 package org.opendaylight.yangtools.util;
9
10 import com.google.common.annotations.VisibleForTesting;
11 import com.google.common.base.Verify;
12 import com.google.common.cache.CacheBuilder;
13 import com.google.common.cache.CacheLoader;
14 import com.google.common.cache.LoadingCache;
15 import com.google.common.collect.ImmutableList;
16 import com.google.common.collect.ImmutableMap;
17 import com.google.common.collect.ImmutableMap.Builder;
18 import com.google.common.collect.ImmutableSet;
19 import java.lang.reflect.Array;
20 import java.util.Collection;
21 import java.util.Iterator;
22 import java.util.List;
23 import java.util.Map;
24
25 final class OffsetMapCache {
26     private static final LoadingCache<Collection<?>, Map<Object, Integer>> CACHE =
27             CacheBuilder.newBuilder().weakValues().build(new CacheLoader<Collection<?>, Map<Object, Integer>>() {
28                 @Override
29                 public Map<Object, Integer> load(final Collection<?> key) {
30                     final Builder<Object, Integer> b = ImmutableMap.builder();
31                     int i = 0;
32
33                     for (Object arg : key) {
34                         b.put(arg, i++);
35                     }
36
37                     return b.build();
38                 }
39     });
40
41     private OffsetMapCache() {
42         throw new UnsupportedOperationException();
43     }
44
45     @SuppressWarnings("unchecked")
46     private static <T> Map<T, Integer> offsets(final Collection<T> args) {
47         return (Map<T, Integer>) CACHE.getUnchecked(args);
48     }
49
50     @VisibleForTesting
51     static void invalidateCache() {
52         CACHE.invalidateAll();
53     }
54
55     static <T> Map<T, Integer> orderedOffsets(final Collection<T> args) {
56         if (args.size() == 1) {
57             return unorderedOffsets(args);
58         }
59
60         return offsets(ImmutableList.copyOf(args));
61     }
62
63     static <T> Map<T, Integer> unorderedOffsets(final Collection<T> args) {
64         if (args instanceof SingletonSet) {
65             return offsets(args);
66         }
67
68         return offsets(ImmutableSet.copyOf(args));
69     }
70
71     private static <K, V> V[] adjustArray(final Map<K, Integer> offsets, final List<K> keys, final V[] array) {
72         @SuppressWarnings("unchecked")
73         final V[] ret = (V[]) Array.newInstance(array.getClass().getComponentType(), array.length);
74
75         int i = 0;
76         for (final K k : keys) {
77             final Integer o = Verify.verifyNotNull(offsets.get(k), "Key %s not present in offsets %s", k, offsets);
78             ret[o] = array[i++];
79         }
80
81         return ret;
82     }
83
84     static <K, V> V[] adjustedArray(final Map<K, Integer> offsets, final List<K> keys, final V[] array) {
85         Verify.verify(offsets.size() == keys.size(), "Offsets %s do not match keys %s", offsets, keys);
86
87         // This relies on the fact that offsets has an ascending iterator
88         final Iterator<K> oi = offsets.keySet().iterator();
89         final Iterator<K> ki = keys.iterator();
90
91         while (oi.hasNext()) {
92             final K o = oi.next();
93             final K k = ki.next();
94             if (!k.equals(o)) {
95                 return adjustArray(offsets, keys, array);
96             }
97         }
98
99         return array;
100     }
101 }