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