001package components.map; 002 003/** 004 * Layered implementations of secondary methods for {@code Map}. 005 * 006 * @param <K> 007 * type of {@code Map} domain (key) entries 008 * @param <V> 009 * type of {@code Map} range (associated value) entries 010 */ 011public abstract class MapSecondary<K, V> implements Map<K, V> { 012 013 /* 014 * Protected members ------------------------------------------------------ 015 */ 016 017 /** 018 * Straightforward implementation of {@code Pair} interface. 019 * 020 * @param <K> 021 * type of {@code Pair} first entry ({@code Map} key entry) 022 * @param <V> 023 * type of {@code Pair} second entry ({@code Map} value entry) 024 */ 025 protected static final class SimplePair<K, V> implements Pair<K, V> { 026 027 /** 028 * The key. 029 */ 030 private final K key; 031 /** 032 * The value. 033 */ 034 private final V value; 035 036 /** 037 * Constructor. 038 * 039 * @param key 040 * the key 041 * @param value 042 * the value 043 */ 044 public SimplePair(K key, V value) { 045 this.key = key; 046 this.value = value; 047 } 048 049 @Override 050 public K key() { 051 return this.key; 052 } 053 054 @Override 055 public V value() { 056 return this.value; 057 } 058 059 @Override 060 public boolean equals(Object obj) { 061 if (obj == this) { 062 return true; 063 } 064 if (obj == null) { 065 return false; 066 } 067 if (!(obj instanceof Pair<?, ?>)) { 068 return false; 069 } 070 Pair<?, ?> pair = (Pair<?, ?>) obj; 071 return this.key.equals(pair.key()) 072 && this.value.equals(pair.value()); 073 } 074 075 @Override 076 public int hashCode() { 077 return this.key.hashCode() + this.value.hashCode(); 078 } 079 080 @Override 081 public String toString() { 082 return "(" + this.key + "," + this.value + ")"; 083 } 084 085 } 086 087 /* 088 * Private members -------------------------------------------------------- 089 */ 090 091 /** 092 * Reports a key associated with {@code value} in {@code m} if such a key 093 * exists, or {@code null} otherwise. 094 * 095 * @param <K> 096 * type of {@code Map} domain (key) entries 097 * @param <V> 098 * type of {@code Map} range (associated value) entries 099 * @param m 100 * the map in which to look for the given value 101 * @param value 102 * the value whose associated key is to be reported 103 * @return a key associated with value 104 * @aliases reference returned by {@code keyForMap}, if such a key exists 105 * @ensures <pre> 106 * if value is in RANGE(m) 107 * then (keyForMap, value) is in m 108 * else [keyForMap is null] 109 * </pre> 110 */ 111 private static <K, V> K keyForMap(Map<K, V> m, V value) { 112 assert m != null : "Violation of: m is not null"; 113 assert value != null : "Violation of: value is not null"; 114 K foundKey = null; 115 /** 116 * @maintains <pre> 117 * value is not in 118 * {key: K, val: V 119 * where ((key, val) is in m and key is in entries(~m.seen)) 120 * (val)} 121 * </pre> 122 */ 123 for (Pair<K, V> pair : m) { 124 if (pair.value().equals(value)) { 125 foundKey = pair.key(); 126 break; 127 } 128 } 129 return foundKey; 130 } 131 132 /* 133 * Public members --------------------------------------------------------- 134 */ 135 136 /* 137 * Common methods (from Object) ------------------------------------------- 138 */ 139 140 @Override 141 public final boolean equals(Object obj) { 142 if (obj == this) { 143 return true; 144 } 145 if (obj == null) { 146 return false; 147 } 148 if (!(obj instanceof Map<?, ?>)) { 149 return false; 150 } 151 /* 152 * This feels like it can't be the right way to do this, but it compiles 153 * with only an "unchecked cast" warning (and the cast cannot fail 154 * because of type erasure). Using wildcard parameters prevents the loop 155 * from compiling. So, it's still a question how it should be done! 156 * Also, there is the technical question of whether it is possible to 157 * tell whether two empty Maps (with different K and V) are equal; seems 158 * not. 159 */ 160 @SuppressWarnings("unchecked") 161 Map<K, V> m = (Map<K, V>) obj; 162 if (this.size() != m.size()) { 163 return false; 164 } 165 /* 166 * |this| = |m|, so every (key, value) pair in this is also in m iff 167 * this = m. 168 */ 169 for (Pair<K, V> pair : this) { 170 if (!m.hasKey(pair.key())) { 171 return false; 172 } else if (!pair.value().equals(m.value(pair.key()))) { 173 return false; 174 } 175 } 176 return true; 177 } 178 179 // CHECKSTYLE: ALLOW THIS METHOD TO BE OVERRIDDEN 180 @Override 181 public int hashCode() { 182 final int a = 37; 183 final int b = 17; 184 int result = 0; 185 /* 186 * This code does not run in O(1) time. Perhaps a better hash function 187 * would be to return the size; there just doesn't seem to be anything 188 * in between! 189 */ 190 for (Pair<K, V> pair : this) { 191 result += a * pair.key().hashCode() + b * pair.value().hashCode(); 192 } 193 return result; 194 } 195 196 // CHECKSTYLE: ALLOW THIS METHOD TO BE OVERRIDDEN 197 @Override 198 public String toString() { 199 StringBuilder result = new StringBuilder("{"); 200 boolean first = true; 201 for (Pair<K, V> pair : this) { 202 if (first) { 203 first = false; 204 } else { 205 result.append(","); 206 } 207 result.append(pair.toString()); 208 } 209 result.append("}"); 210 return result.toString(); 211 } 212 213 /* 214 * Other non-kernel methods ----------------------------------------------- 215 */ 216 217 // CHECKSTYLE: ALLOW THIS METHOD TO BE OVERRIDDEN 218 @Override 219 public V replaceValue(K key, V value) { 220 assert key != null : "Violation of: key is not null"; 221 assert value != null : "Violation of: value is not null"; 222 assert this.hasKey(key) : "Violation of: key is in DOMAIN(this)"; 223 MapSecondary.Pair<K, V> p = this.remove(key); 224 this.add(p.key(), value); 225 return p.value(); 226 } 227 228 // CHECKSTYLE: ALLOW THIS METHOD TO BE OVERRIDDEN 229 @Override 230 public K key(V value) { 231 assert value != null : "Violation of: value is not null"; 232 K foundKey = keyForMap(this, value); 233 assert foundKey != null : "Violation of: value is in RANGE(this)"; 234 return foundKey; 235 } 236 237 // CHECKSTYLE: ALLOW THIS METHOD TO BE OVERRIDDEN 238 @Override 239 public boolean hasValue(V value) { 240 assert value != null : "Violation of: value is not null"; 241 K foundKey = keyForMap(this, value); 242 return foundKey != null; 243 } 244 245 // CHECKSTYLE: ALLOW THIS METHOD TO BE OVERRIDDEN 246 @Override 247 public boolean sharesKeyWith(Map<K, V> m) { 248 assert m != null : "Violation of: m is not null"; 249 assert m != this : "Violation of: m is not this"; 250 boolean found = false; 251 /** 252 * @maintains <pre> 253 * if key is in entries(~this.seen) 254 * then key is not in DOMAIN(m) 255 * </pre> 256 */ 257 for (Pair<K, V> pair : this) { 258 if (m.hasKey(pair.key())) { 259 found = true; 260 break; 261 } 262 } 263 return found; 264 } 265 266 // CHECKSTYLE: ALLOW THIS METHOD TO BE OVERRIDDEN 267 @Override 268 public void combineWith(Map<K, V> m) { 269 assert m != null : "Violation of: m is not null"; 270 assert m != this : "Violation of: m is not this"; 271 assert !this.sharesKeyWith(m) : "Violation of: " 272 + "DOMAIN(this) intersection DOMAIN(m) = {}"; 273 /** 274 * @updates this 275 * @maintains <pre> 276 * this = #this union 277 * {key: K, value: V 278 * where ((key, value) is in m and 279 * key is in entries(~m.seen)) 280 * ((key, value))} 281 * </pre> 282 * @decreases |~m.unseen| 283 */ 284 for (Pair<K, V> pair : m) { 285 this.add(pair.key(), pair.value()); 286 } 287 m.clear(); 288 } 289 290}