View Javadoc
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&lt;K,V&gt;; 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&lt;E&gt; getMap()
57       * {
58       *     return (HashMap&lt;E&gt;) 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     /** {@inheritDoc} */
158     @Override
159     @SuppressWarnings("checkstyle:designforextension")
160     public String toString()
161     {
162         return "Immutable[" + this.map.toString() + "]";
163     }
164 }