1 /*
   2  * Copyright (c) 2015, 2018, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.  Oracle designates this
   8  * particular file as subject to the "Classpath" exception as provided
   9  * by Oracle in the LICENSE file that accompanied this code.
  10  *
  11  * This code is distributed in the hope that it will be useful, but WITHOUT
  12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  14  * version 2 for more details (a copy is included in the LICENSE file that
  15  * accompanied this code).
  16  *
  17  * You should have received a copy of the GNU General Public License version
  18  * 2 along with this work; if not, write to the Free Software Foundation,
  19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  20  *
  21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  22  * or visit www.oracle.com if you need additional information or have any
  23  * questions.
  24  */
  25 
  26 package sun.awt;
  27 
  28 import java.lang.ref.Reference;
  29 import java.lang.ref.ReferenceQueue;
  30 import java.lang.ref.WeakReference;
  31 import java.util.*;
  32 
  33 // A weak key reference hash map that uses System.identityHashCode() and "=="
  34 // instead of hashCode() and equals(Object)
  35 class WeakIdentityHashMap<K, V> implements Map<K, V> {
  36     private final Map<WeakKey<K>, V> map;
  37     private final transient ReferenceQueue<K> queue = new ReferenceQueue<K>();
  38 
  39     /**
  40      * Constructs a new, empty identity hash map with a default initial
  41      * size (16).
  42      */
  43     public WeakIdentityHashMap() {
  44         map = new HashMap<>(16);
  45     }
  46 
  47     /**
  48      * Constructs a new, empty identity map with the specified initial size.
  49      */
  50     public WeakIdentityHashMap(int initialSize) {
  51         map = new HashMap<>(initialSize);
  52     }
  53 
  54     private Map<WeakKey<K>, V> getMap() {
  55         for(Reference<? extends K> ref; (ref = this.queue.poll()) != null;) {
  56             map.remove(ref);
  57         }
  58         return map;
  59     }
  60 
  61     @Override
  62     public int size() {
  63         return getMap().size();
  64     }
  65 
  66     @Override
  67     public boolean isEmpty() {
  68         return getMap().isEmpty();
  69     }
  70 
  71     @Override
  72     public boolean containsKey(Object key) {
  73         return getMap().containsKey(new WeakKey<>(key, null));
  74     }
  75 
  76     @Override
  77     public boolean containsValue(Object value) {
  78         return getMap().containsValue(value);
  79     }
  80 
  81     @Override
  82     public V get(Object key) {
  83         return getMap().get(new WeakKey<>(key, null));
  84     }
  85 
  86     @Override
  87     public V put(K key, V value) {
  88         return getMap().put(new WeakKey<K>(key, queue), value);
  89     }
  90 
  91     @Override
  92     public V remove(Object key) {
  93         return getMap().remove(new WeakKey<>(key, null));
  94     }
  95 
  96     @Override
  97     public void putAll(Map<? extends K, ? extends V> m) {
  98         for (Entry<? extends K, ? extends V> entry : m.entrySet()) {
  99             put(entry.getKey(), entry.getValue());
 100         }
 101     }
 102 
 103     @Override
 104     public void clear() {
 105         getMap().clear();
 106     }
 107 
 108     @Override
 109     public Set<K> keySet() {
 110         return new AbstractSet<K>() {
 111             @Override
 112             public Iterator<K> iterator() {
 113                 return new Iterator<K>() {
 114                     private K next;
 115                     Iterator<WeakKey<K>> iterator = getMap().keySet().iterator();
 116 
 117                     @Override
 118                     public boolean hasNext() {
 119                         while (iterator.hasNext()) {
 120                             if ((next = iterator.next().get()) != null) {
 121                                 return true;
 122                             }
 123                         }
 124                         return false;
 125                     }
 126 
 127                     @Override
 128                     public K next() {
 129                         if(next == null && !hasNext()) {
 130                             throw new NoSuchElementException();
 131                         }
 132                         K ret = next;
 133                         next = null;
 134                         return ret;
 135                     }
 136                 };
 137             }
 138 
 139             @Override
 140             public int size() {
 141                 return getMap().keySet().size();
 142             }
 143         };
 144     }
 145 
 146     @Override
 147     public Collection<V> values() {
 148         return getMap().values();
 149     }
 150 
 151     @Override
 152     public Set<Entry<K, V>> entrySet() {
 153         return new AbstractSet<Entry<K, V>>() {
 154             @Override
 155             public Iterator<Entry<K, V>> iterator() {
 156                 final Iterator<Entry<WeakKey<K>, V>> iterator = getMap().entrySet().iterator();
 157                 return new Iterator<Entry<K, V>>() {
 158                     @Override
 159                     public boolean hasNext() {
 160                         return iterator.hasNext();
 161                     }
 162 
 163                     @Override
 164                     public Entry<K, V> next() {
 165                         return new Entry<K, V>() {
 166                             Entry<WeakKey<K>, V> entry = iterator.next();
 167 
 168                             @Override
 169                             public K getKey() {
 170                                 return entry.getKey().get();
 171                             }
 172 
 173                             @Override
 174                             public V getValue() {
 175                                 return entry.getValue();
 176                             }
 177 
 178                             @Override
 179                             public V setValue(V value) {
 180                                 return null;
 181                             }
 182                         };
 183                     }
 184                 };
 185             }
 186 
 187             @Override
 188             public int size() {
 189                 return getMap().entrySet().size();
 190             }
 191         };
 192     }
 193 
 194     private static class WeakKey<K> extends WeakReference<K>  {
 195         private final int hash;
 196 
 197         WeakKey(K key, ReferenceQueue <K> q) {
 198             super(key, q);
 199             hash = System.identityHashCode(key);
 200         }
 201 
 202         @Override
 203         public boolean equals(Object o) {
 204             if(this == o) {
 205                 return true;
 206             } else if( o instanceof WeakKey ) {
 207                 return get() == ((WeakKey)o).get();
 208             } else {
 209                 return false;
 210             }
 211         }
 212 
 213         @Override
 214         public int hashCode() {
 215             return hash;
 216         }
 217     }
 218 
 219 
 220 }