1 package org.opentrafficsim.core.gtu;
2
3 import java.util.Arrays;
4 import java.util.LinkedHashMap;
5 import java.util.List;
6 import java.util.Map;
7 import java.util.Set;
8 import java.util.function.Supplier;
9
10 import nl.tudelft.simulation.language.Throw;
11
12 /**
13 * Utility class to cache data based on a variable (between cache instances) number of keys of any type. This replaces nested
14 * {@code Map}s.
15 * <p>
16 * Copyright (c) 2013-2018 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
17 * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
18 * <p>
19 * @version $Revision$, $LastChangedDate$, by $Author$, initial version 20 apr. 2018 <br>
20 * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
21 * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
22 * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
23 * @param <T> value type
24 */
25 public class NestedCache<T>
26 {
27
28 /** Key types. */
29 private final Class<?>[] types;
30
31 /** Map with cache. */
32 private final Map<Object, Object> map = new LinkedHashMap<>();
33
34 /**
35 * Constructor.
36 * @param types Class...; types
37 */
38 public NestedCache(final Class<?>... types)
39 {
40 this.types = types;
41 }
42
43 /**
44 * Returns a value.
45 * @param supplier Supplier; supplier of {@code T} for if it wasn't cached yet
46 * @param keys List; list of key objects
47 * @return T; value
48 */
49 public T getValue(final Supplier<T> supplier, final Object... keys)
50 {
51 return getValue(supplier, Arrays.asList(keys));
52 }
53
54 /**
55 * Returns a value. This uses a {@code List} rather than an array to allow flexible inner workings.
56 * @param supplier Supplier; supplier of {@code T} for if it wasn't cached yet
57 * @param keys List; list of key objects
58 * @return T; value
59 */
60 @SuppressWarnings("unchecked")
61 private T getValue(final Supplier<T> supplier, final List<Object> keys)
62 {
63 Throw.when(keys.size() != this.types.length, IllegalArgumentException.class, "Incorrect number of keys.");
64 Throw.when(keys.get(0) != null && !this.types[0].isAssignableFrom(keys.get(0).getClass()),
65 IllegalArgumentException.class, "Key %s is not of %s.", keys.get(0), this.types[0]);
66 Object sub = this.map.get(keys.get(0));
67 if (this.types.length == 1)
68 {
69 if (sub == null)
70 {
71 sub = supplier.get();
72 this.map.put(keys.get(0), sub);
73 }
74 return (T) sub;
75 }
76 if (sub == null)
77 {
78 // create sub-NestedCache with 1 less key
79 Class<Object>[] subTypes = new Class[this.types.length - 1];
80 System.arraycopy(this.types, 1, subTypes, 0, this.types.length - 1);
81 sub = new NestedCache<T>(subTypes);
82 this.map.put(keys.get(0), sub);
83 }
84 // return from sub-NestedCache with 1 less key
85 return ((NestedCache<T>) sub).getValue(supplier, keys.subList(1, keys.size()));
86 }
87
88 /**
89 * Return set of key objects on this level.
90 * @return Set; set of key objects on this level
91 */
92 public Set<Object> getKeys()
93 {
94 return this.map.keySet();
95 }
96
97 /**
98 * Return branch for key.
99 * @param key Object; key
100 * @return NestedCache; branch for key
101 * @throws IllegalStateException if this is not a branch level
102 */
103 @SuppressWarnings("unchecked")
104 public NestedCache<T> getChild(final Object key) throws IllegalStateException
105 {
106 Throw.when(this.types.length < 2, IllegalStateException.class, "Children can only be obtained on branch levels.");
107 return (NestedCache<T>) this.map.get(key);
108 }
109
110 /**
111 * Return value for key.
112 * @param key Object; key
113 * @return T; value for key
114 * @throws IllegalStateException if this is not a leaf level
115 */
116 @SuppressWarnings("unchecked")
117 public T getValue(final Object key) throws IllegalStateException
118 {
119 Throw.when(this.types.length != 1, IllegalStateException.class, "Values can only be obtained on leaf levels.");
120 return (T) this.map.get(key);
121 }
122
123 /** {@inheritDoc} */
124 @Override
125 public String toString()
126 {
127 return "NestedCache [types=" + Arrays.toString(this.types) + ", map=" + this.map + "]";
128 }
129
130 }