View Javadoc
1   package org.djutils.immutablecollections;
2   
3   import java.util.LinkedHashSet;
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-2025 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   * @param <K> the key type of content of this Map
20   * @param <V> the value type of content of this Map
21   */
22  public abstract class ImmutableAbstractMap<K, V> implements ImmutableMap<K, V>
23  {
24      /** the map that is wrapped, without giving access to methods that can change it. */
25      private final Map<K, V> map;
26  
27      /** COPY stores a safe, internal copy of the collection; WRAP stores a pointer to the original collection. */
28      @SuppressWarnings("checkstyle:visibilitymodifier")
29      protected final Immutable copyOrWrap;
30  
31      /** the cached values. */
32      @SuppressWarnings("checkstyle:visibilitymodifier")
33      protected ImmutableCollection<V> cachedValues = null;
34  
35      /**
36       * Construct an abstract immutable map. Make sure that the argument is a safe copy of the map of the right type! Copying
37       * does not take place in the Abstract class!
38       * @param map a safe copy of the map to use for the immutable map
39       * @param copyOrWrap indicate whether the immutable is a copy or a wrap
40       */
41      protected ImmutableAbstractMap(final Map<K, V> map, final Immutable copyOrWrap)
42      {
43          Throw.whenNull(copyOrWrap, "the copyOrWrap argument should be Immutable.COPY or Immutable.WRAP");
44          this.copyOrWrap = copyOrWrap;
45          Throw.whenNull(map, "the map argument cannot be null");
46          this.map = map;
47      }
48  
49      /**
50       * Return the raw underlying map.
51       * @return the raw underlying map
52       */
53      @SuppressWarnings("checkstyle:designforextension")
54      protected Map<K, V> getUnderlyingMap()
55      {
56          return this.map;
57      }
58  
59      @Override
60      public final int size()
61      {
62          return this.map.size();
63      }
64  
65      @Override
66      public final boolean isEmpty()
67      {
68          return this.map.isEmpty();
69      }
70  
71      @Override
72      public final boolean containsKey(final Object key)
73      {
74          return this.map.containsKey(key);
75      }
76  
77      @Override
78      public final boolean containsValue(final Object value)
79      {
80          return this.map.containsValue(value);
81      }
82  
83      @Override
84      public final V get(final Object key)
85      {
86          return this.map.get(key);
87      }
88  
89      @Override
90      public ImmutableCollection<V> values()
91      {
92          if (this.cachedValues == null)
93          {
94              Set<V> immutableValues = new LinkedHashSet<>(getUnderlyingMap().values());
95              this.cachedValues = new ImmutableHashSet<>(immutableValues, Immutable.WRAP);
96          }
97          return this.cachedValues;
98      }
99  
100     @Override
101     public final boolean isWrap()
102     {
103         return this.copyOrWrap.isWrap();
104     }
105 
106     @Override
107     @SuppressWarnings("checkstyle:designforextension")
108     public int hashCode()
109     {
110         final int prime = 31;
111         int result = 1;
112         result = prime * result + ((this.map == null) ? 0 : this.map.hashCode());
113         return result;
114     }
115 
116     @Override
117     @SuppressWarnings({"checkstyle:designforextension", "checkstyle:needbraces"})
118     public boolean equals(final Object obj)
119     {
120         if (this == obj)
121             return true;
122         if (obj == null)
123             return false;
124         if (getClass() != obj.getClass())
125             return false;
126         ImmutableAbstractMap<?, ?> other = (ImmutableAbstractMap<?, ?>) obj;
127         if (this.map == null)
128         {
129             if (other.map != null)
130                 return false;
131         }
132         else if (!this.map.equals(other.map))
133             return false;
134         return true;
135     }
136 
137 }