Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
PojavLauncherTeam
GitHub Repository: PojavLauncherTeam/mobile
Path: blob/master/src/java.desktop/share/classes/sun/awt/SoftCache.java
41152 views
1
/*
2
* Copyright (c) 1998, 2014, 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.SoftReference;
29
import java.lang.ref.ReferenceQueue;
30
31
import java.util.Iterator;
32
import java.util.Map;
33
import java.util.AbstractMap;
34
import java.util.HashMap;
35
import java.util.Set;
36
import java.util.AbstractSet;
37
import java.util.NoSuchElementException;
38
39
40
/**
41
* A memory-sensitive implementation of the <code>Map</code> interface.
42
*
43
* <p> A <code>SoftCache</code> object uses {@link java.lang.ref.SoftReference
44
* soft references} to implement a memory-sensitive hash map. If the garbage
45
* collector determines at a certain point in time that a value object in a
46
* <code>SoftCache</code> entry is no longer strongly reachable, then it may
47
* remove that entry in order to release the memory occupied by the value
48
* object. All <code>SoftCache</code> objects are guaranteed to be completely
49
* cleared before the virtual machine will throw an
50
* <code>OutOfMemoryError</code>. Because of this automatic clearing feature,
51
* the behavior of this class is somewhat different from that of other
52
* <code>Map</code> implementations.
53
*
54
* <p> Both null values and the null key are supported. This class has the
55
* same performance characteristics as the <code>HashMap</code> class, and has
56
* the same efficiency parameters of <em>initial capacity</em> and <em>load
57
* factor</em>.
58
*
59
* <p> Like most collection classes, this class is not synchronized. A
60
* synchronized <code>SoftCache</code> may be constructed using the
61
* <code>Collections.synchronizedMap</code> method.
62
*
63
* <p> In typical usage this class will be subclassed and the <code>fill</code>
64
* method will be overridden. When the <code>get</code> method is invoked on a
65
* key for which there is no mapping in the cache, it will in turn invoke the
66
* <code>fill</code> method on that key in an attempt to construct a
67
* corresponding value. If the <code>fill</code> method returns such a value
68
* then the cache will be updated and the new value will be returned. Thus,
69
* for example, a simple URL-content cache can be constructed as follows:
70
*
71
* <pre>
72
* public class URLCache extends SoftCache {
73
* protected Object fill(Object key) {
74
* return ((URL)key).getContent();
75
* }
76
* }
77
* </pre>
78
*
79
* <p> The behavior of the <code>SoftCache</code> class depends in part upon
80
* the actions of the garbage collector, so several familiar (though not
81
* required) <code>Map</code> invariants do not hold for this class. <p>
82
* Because entries are removed from a <code>SoftCache</code> in response to
83
* dynamic advice from the garbage collector, a <code>SoftCache</code> may
84
* behave as though an unknown thread is silently removing entries. In
85
* particular, even if you synchronize on a <code>SoftCache</code> instance and
86
* invoke none of its mutator methods, it is possible for the <code>size</code>
87
* method to return smaller values over time, for the <code>isEmpty</code>
88
* method to return <code>false</code> and then <code>true</code>, for the
89
* <code>containsKey</code> method to return <code>true</code> and later
90
* <code>false</code> for a given key, for the <code>get</code> method to
91
* return a value for a given key but later return <code>null</code>, for the
92
* <code>put</code> method to return <code>null</code> and the
93
* <code>remove</code> method to return <code>false</code> for a key that
94
* previously appeared to be in the map, and for successive examinations of the
95
* key set, the value set, and the entry set to yield successively smaller
96
* numbers of elements.
97
*
98
* @author Mark Reinhold
99
* @since 1.2
100
* @see java.util.HashMap
101
* @see java.lang.ref.SoftReference
102
* @deprecated No direct replacement; {@link java.util.WeakHashMap}
103
* addresses a related by different use-case.
104
*/
105
106
@Deprecated
107
public class SoftCache extends AbstractMap<Object, Object> implements Map<Object, Object> {
108
109
/* The basic idea of this implementation is to maintain an internal HashMap
110
that maps keys to soft references whose referents are the keys' values;
111
the various accessor methods dereference these soft references before
112
returning values. Because we don't have access to the innards of the
113
HashMap, each soft reference must contain the key that maps to it so
114
that the processQueue method can remove keys whose values have been
115
discarded. Thus the HashMap actually maps keys to instances of the
116
ValueCell class, which is a simple extension of the SoftReference class.
117
*/
118
119
120
private static class ValueCell extends SoftReference<Object> {
121
private static Object INVALID_KEY = new Object();
122
private static int dropped = 0;
123
private Object key;
124
125
private ValueCell(Object key, Object value, ReferenceQueue<Object> queue) {
126
super(value, queue);
127
this.key = key;
128
}
129
130
private static ValueCell create(Object key, Object value,
131
ReferenceQueue<Object> queue)
132
{
133
if (value == null) return null;
134
return new ValueCell(key, value, queue);
135
}
136
137
private static Object strip(Object val, boolean drop) {
138
if (val == null) return null;
139
ValueCell vc = (ValueCell)val;
140
Object o = vc.get();
141
if (drop) vc.drop();
142
return o;
143
}
144
145
private boolean isValid() {
146
return (key != INVALID_KEY);
147
}
148
149
private void drop() {
150
super.clear();
151
key = INVALID_KEY;
152
dropped++;
153
}
154
155
}
156
157
158
/* Hash table mapping keys to ValueCells */
159
private Map<Object, Object> hash;
160
161
/* Reference queue for cleared ValueCells */
162
private ReferenceQueue<Object> queue = new ReferenceQueue<>();
163
164
165
/* Process any ValueCells that have been cleared and enqueued by the
166
garbage collector. This method should be invoked once by each public
167
mutator in this class. We don't invoke this method in public accessors
168
because that can lead to surprising ConcurrentModificationExceptions.
169
*/
170
private void processQueue() {
171
ValueCell vc;
172
while ((vc = (ValueCell)queue.poll()) != null) {
173
if (vc.isValid()) hash.remove(vc.key);
174
else ValueCell.dropped--;
175
}
176
}
177
178
179
/* -- Constructors -- */
180
181
/**
182
* Construct a new, empty <code>SoftCache</code> with the given
183
* initial capacity and the given load factor.
184
*
185
* @param initialCapacity The initial capacity of the cache
186
*
187
* @param loadFactor A number between 0.0 and 1.0
188
*
189
* @throws IllegalArgumentException If the initial capacity is less than
190
* or equal to zero, or if the load
191
* factor is less than zero
192
*/
193
public SoftCache(int initialCapacity, float loadFactor) {
194
hash = new HashMap<>(initialCapacity, loadFactor);
195
}
196
197
/**
198
* Construct a new, empty <code>SoftCache</code> with the given
199
* initial capacity and the default load factor.
200
*
201
* @param initialCapacity The initial capacity of the cache
202
*
203
* @throws IllegalArgumentException If the initial capacity is less than
204
* or equal to zero
205
*/
206
public SoftCache(int initialCapacity) {
207
hash = new HashMap<>(initialCapacity);
208
}
209
210
/**
211
* Construct a new, empty <code>SoftCache</code> with the default
212
* capacity and the default load factor.
213
*/
214
public SoftCache() {
215
hash = new HashMap<>();
216
}
217
218
219
/* -- Simple queries -- */
220
221
/**
222
* Return the number of key-value mappings in this cache. The time
223
* required by this operation is linear in the size of the map.
224
*/
225
public int size() {
226
return entrySet().size();
227
}
228
229
/**
230
* Return <code>true</code> if this cache contains no key-value mappings.
231
*/
232
public boolean isEmpty() {
233
return entrySet().isEmpty();
234
}
235
236
/**
237
* Return <code>true</code> if this cache contains a mapping for the
238
* specified key. If there is no mapping for the key, this method will not
239
* attempt to construct one by invoking the <code>fill</code> method.
240
*
241
* @param key The key whose presence in the cache is to be tested
242
*/
243
public boolean containsKey(Object key) {
244
return ValueCell.strip(hash.get(key), false) != null;
245
}
246
247
248
/* -- Lookup and modification operations -- */
249
250
/**
251
* Create a value object for the given <code>key</code>. This method is
252
* invoked by the <code>get</code> method when there is no entry for
253
* <code>key</code>. If this method returns a non-<code>null</code> value,
254
* then the cache will be updated to map <code>key</code> to that value,
255
* and that value will be returned by the <code>get</code> method.
256
*
257
* <p> The default implementation of this method simply returns
258
* <code>null</code> for every <code>key</code> value. A subclass may
259
* override this method to provide more useful behavior.
260
*
261
* @param key The key for which a value is to be computed
262
*
263
* @return A value for <code>key</code>, or <code>null</code> if one
264
* could not be computed
265
* @see #get
266
*/
267
protected Object fill(Object key) {
268
return null;
269
}
270
271
/**
272
* Return the value to which this cache maps the specified
273
* <code>key</code>. If the cache does not presently contain a value for
274
* this key, then invoke the <code>fill</code> method in an attempt to
275
* compute such a value. If that method returns a non-<code>null</code>
276
* value, then update the cache and return the new value. Otherwise,
277
* return <code>null</code>.
278
*
279
* <p> Note that because this method may update the cache, it is considered
280
* a mutator and may cause <code>ConcurrentModificationException</code>s to
281
* be thrown if invoked while an iterator is in use.
282
*
283
* @param key The key whose associated value, if any, is to be returned
284
*
285
* @see #fill
286
*/
287
public Object get(Object key) {
288
processQueue();
289
Object v = hash.get(key);
290
if (v == null) {
291
v = fill(key);
292
if (v != null) {
293
hash.put(key, ValueCell.create(key, v, queue));
294
return v;
295
}
296
}
297
return ValueCell.strip(v, false);
298
}
299
300
/**
301
* Update this cache so that the given <code>key</code> maps to the given
302
* <code>value</code>. If the cache previously contained a mapping for
303
* <code>key</code> then that mapping is replaced and the old value is
304
* returned.
305
*
306
* @param key The key that is to be mapped to the given
307
* <code>value</code>
308
* @param value The value to which the given <code>key</code> is to be
309
* mapped
310
*
311
* @return The previous value to which this key was mapped, or
312
* <code>null</code> if there was no mapping for the key
313
*/
314
public Object put(Object key, Object value) {
315
processQueue();
316
ValueCell vc = ValueCell.create(key, value, queue);
317
return ValueCell.strip(hash.put(key, vc), true);
318
}
319
320
/**
321
* Remove the mapping for the given <code>key</code> from this cache, if
322
* present.
323
*
324
* @param key The key whose mapping is to be removed
325
*
326
* @return The value to which this key was mapped, or <code>null</code> if
327
* there was no mapping for the key
328
*/
329
public Object remove(Object key) {
330
processQueue();
331
return ValueCell.strip(hash.remove(key), true);
332
}
333
334
/**
335
* Remove all mappings from this cache.
336
*/
337
public void clear() {
338
processQueue();
339
hash.clear();
340
}
341
342
343
/* -- Views -- */
344
345
private static boolean valEquals(Object o1, Object o2) {
346
return (o1 == null) ? (o2 == null) : o1.equals(o2);
347
}
348
349
350
/* Internal class for entries.
351
Because it uses SoftCache.this.queue, this class cannot be static.
352
*/
353
private class Entry implements Map.Entry<Object, Object> {
354
private Map.Entry<Object, Object> ent;
355
private Object value; /* Strong reference to value, to prevent the GC
356
from flushing the value while this Entry
357
exists */
358
359
Entry(Map.Entry<Object, Object> ent, Object value) {
360
this.ent = ent;
361
this.value = value;
362
}
363
364
public Object getKey() {
365
return ent.getKey();
366
}
367
368
public Object getValue() {
369
return value;
370
}
371
372
public Object setValue(Object value) {
373
return ent.setValue(ValueCell.create(ent.getKey(), value, queue));
374
}
375
376
@SuppressWarnings("unchecked")
377
public boolean equals(Object o) {
378
if (! (o instanceof Map.Entry)) return false;
379
Map.Entry<Object, Object> e = (Map.Entry<Object, Object>)o;
380
return (valEquals(ent.getKey(), e.getKey())
381
&& valEquals(value, e.getValue()));
382
}
383
384
public int hashCode() {
385
Object k;
386
return ((((k = getKey()) == null) ? 0 : k.hashCode())
387
^ ((value == null) ? 0 : value.hashCode()));
388
}
389
390
}
391
392
393
/* Internal class for entry sets */
394
private class EntrySet extends AbstractSet<Map.Entry<Object, Object>> {
395
Set<Map.Entry<Object, Object>> hashEntries = hash.entrySet();
396
397
public Iterator<Map.Entry<Object, Object>> iterator() {
398
399
return new Iterator<Map.Entry<Object, Object>>() {
400
Iterator<Map.Entry<Object, Object>> hashIterator = hashEntries.iterator();
401
Entry next = null;
402
403
public boolean hasNext() {
404
while (hashIterator.hasNext()) {
405
Map.Entry<Object, Object> ent = hashIterator.next();
406
ValueCell vc = (ValueCell)ent.getValue();
407
Object v = null;
408
if ((vc != null) && ((v = vc.get()) == null)) {
409
/* Value has been flushed by GC */
410
continue;
411
}
412
next = new Entry(ent, v);
413
return true;
414
}
415
return false;
416
}
417
418
public Map.Entry<Object, Object> next() {
419
if ((next == null) && !hasNext())
420
throw new NoSuchElementException();
421
Entry e = next;
422
next = null;
423
return e;
424
}
425
426
public void remove() {
427
hashIterator.remove();
428
}
429
430
};
431
}
432
433
public boolean isEmpty() {
434
return !(iterator().hasNext());
435
}
436
437
public int size() {
438
int j = 0;
439
for (Iterator<Map.Entry<Object, Object>> i = iterator(); i.hasNext(); i.next()) j++;
440
return j;
441
}
442
443
public boolean remove(Object o) {
444
processQueue();
445
if (o instanceof Entry) return hashEntries.remove(((Entry)o).ent);
446
else return false;
447
}
448
449
}
450
451
452
private Set<Map.Entry<Object, Object>> entrySet = null;
453
454
/**
455
* Return a <code>Set</code> view of the mappings in this cache.
456
*/
457
public Set<Map.Entry<Object, Object>> entrySet() {
458
if (entrySet == null) entrySet = new EntrySet();
459
return entrySet;
460
}
461
462
}
463
464