1 package org.djutils.immutablecollections;
2
3 import java.util.HashSet;
4 import java.util.Map;
5 import java.util.Set;
6
7 import org.djutils.exceptions.Throw;
8
9 /**
10 * An abstract base class for an immutable wrapper for a Map.
11 * <p>
12 * Copyright (c) 2016-2019 Delft University of Technology, Jaffalaan 5, 2628 BX Delft, the Netherlands. All rights reserved. See
13 * for project information <a href="https://djutils.org" target="_blank"> https://djutils.org</a>. The DJUTILS project is
14 * distributed under a three-clause BSD-style license, which can be found at
15 * <a href="https://djutils.org/docs/license.html" target="_blank"> https://djutils.org/docs/license.html</a>.
16 * </p>
17 * @author <a href="https://www.tudelft.nl/averbraeck">Alexander Verbraeck</a>
18 * @author <a href="https://www.tudelft.nl/staff/p.knoppers/">Peter Knoppers</a>
19 * @author <a href="https://www.transport.citg.tudelft.nl">Wouter Schakel</a>
20 * @param <K> the key type of content of this Map
21 * @param <V> the value type of content of this Map
22 */
23 public abstract class ImmutableAbstractMap<K, V> implements ImmutableMap<K, V>
24 {
25 /** */
26 private static final long serialVersionUID = 20160507L;
27
28 /** the map that is wrapped, without giving access to methods that can change it. */
29 private final Map<K, V> map;
30
31 /** COPY stores a safe, internal copy of the collection; WRAP stores a pointer to the original collection. */
32 protected final Immutable copyOrWrap;
33
34 /** the cached values. */
35 protected ImmutableCollection<V> cachedValues = null;
36
37 /**
38 * Construct an abstract immutable map. Make sure that the argument is a safe copy of the map of the right type! Copying
39 * does not take place in the Abstract class!
40 * @param map Map<K,V>; a safe copy of the map to use for the immutable map
41 * @param copyOrWrap Immutable; indicate whether the immutable is a copy or a wrap
42 */
43 protected ImmutableAbstractMap(final Map<K, V> map, final Immutable copyOrWrap)
44 {
45 Throw.whenNull(copyOrWrap, "the copyOrWrap argument should be Immutable.COPY or Immutable.WRAP");
46 this.copyOrWrap = copyOrWrap;
47 Throw.whenNull(map, "the map argument cannot be null");
48 this.map = map;
49 }
50
51 /**
52 * Prepare the map of the right type for use a subclass. Implement e.g. as follows:
53 *
54 * <pre>
55 * {@literal @}Override
56 * protected HashMap<E> getMap()
57 * {
58 * return (HashMap<E>) super.getMap();
59 * }
60 * </pre>
61 *
62 * @return the map of the right type for use a subclass
63 */
64 @SuppressWarnings("checkstyle:designforextension")
65 protected Map<K, V> getMap()
66 {
67 return this.map;
68 }
69
70 /** {@inheritDoc} */
71 @Override
72 public final int size()
73 {
74 return this.map.size();
75 }
76
77 /** {@inheritDoc} */
78 @Override
79 public final boolean isEmpty()
80 {
81 return this.map.isEmpty();
82 }
83
84 /** {@inheritDoc} */
85 @Override
86 public final boolean containsKey(final Object key)
87 {
88 return this.map.containsKey(key);
89 }
90
91 /** {@inheritDoc} */
92 @Override
93 public final boolean containsValue(final Object value)
94 {
95 return this.map.containsValue(value);
96 }
97
98 /** {@inheritDoc} */
99 @Override
100 public final V get(final Object key)
101 {
102 return this.map.get(key);
103 }
104
105 /** {@inheritDoc} */
106 @Override
107 public ImmutableCollection<V> values()
108 {
109 if (this.cachedValues == null)
110 {
111 Set<V> immutableValues = new HashSet<>(getMap().values());
112 this.cachedValues = new ImmutableHashSet<>(immutableValues, Immutable.WRAP);
113 }
114 return this.cachedValues;
115 }
116
117 /** {@inheritDoc} */
118 @Override
119 public final boolean isWrap()
120 {
121 return this.copyOrWrap.isWrap();
122 }
123
124 /** {@inheritDoc} */
125 @Override
126 @SuppressWarnings("checkstyle:designforextension")
127 public int hashCode()
128 {
129 final int prime = 31;
130 int result = 1;
131 result = prime * result + ((this.map == null) ? 0 : this.map.hashCode());
132 return result;
133 }
134
135 /** {@inheritDoc} */
136 @Override
137 @SuppressWarnings({"checkstyle:designforextension", "checkstyle:needbraces"})
138 public boolean equals(final Object obj)
139 {
140 if (this == obj)
141 return true;
142 if (obj == null)
143 return false;
144 if (getClass() != obj.getClass())
145 return false;
146 ImmutableAbstractMap<?, ?> other = (ImmutableAbstractMap<?, ?>) obj;
147 if (this.map == null)
148 {
149 if (other.map != null)
150 return false;
151 }
152 else if (!this.map.equals(other.map))
153 return false;
154 return true;
155 }
156
157 }