~vil/pydev/upstream

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
package org.python.pydev.core.cache;

import java.io.Serializable;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.Map.Entry;


/**
 * If the cache is to be used by multiple threads,
 * the cache must be wrapped with code to synchronize the methods
 * cache = (Map)Collections.synchronizedMap(cache);
 * 
 * (it is actually serializable or not depending on its keys and values)
 */
public class LRUCache<Key, Val> implements Cache<Key, Val>, Serializable{

	protected int maxSize;

	private static final long serialVersionUID = 1L;
	private transient int removeEntries;
	private transient int initialMaxSize;
	
	public LRUCache(int maxSize){
		this.maxSize = maxSize;
		cache = createMap(maxSize);
	}

	protected LinkedHashMap<Key, Val> createMap(int maxSize) {
		return new LinkedHashMap<Key,Val>(maxSize+1, .75F, true) {
	        // This method is called just after a new entry has been added
	        public boolean removeEldestEntry(Map.Entry eldest) {
	            return size() > LRUCache.this.maxSize;
	        }
	    };
	}
	
	public void stopGrowAsNeeded(){
		synchronized(this){
			removeEntries--;
			if(removeEntries == 0){
				maxSize = initialMaxSize;
				Iterator<Entry<Key, Val>> iter = cache.entrySet().iterator();
				//go to the position of the 'eldest' entries
				for (int i = 0; i < cache.size() - maxSize; i++) {
					iter.next();
				}
				//and now remove the eldest entries
				while(cache.size() > maxSize && iter.hasNext()){
					iter.next();
					iter.remove();
				}
			}
		}
	}
	
	/**
	 * Can be used to stop removing oldest entries (this can be useful when doing some 'local' operations that want
	 * to be faster, having the new max size passed as the new 'value'. Note that other calls to this method will
	 * be ignored and will not change that limit). 
	 * 
	 * Also, the link between startGrowAsNeeded and stopGrowAsNeeded should be synchronized to avoid that one thread
	 * raises and another one stopping that raise (so, use this with care).
	 */
	public void startGrowAsNeeded(int newSize){
		if(removeEntries > 0){
			throw new RuntimeException("There is alrdeady a new size in action. This class should be synched for this access.");
		}
		synchronized(this){
			removeEntries++;
			if(removeEntries == 1){
				//start
				initialMaxSize = maxSize;
				maxSize = newSize;
			}
		}
	}
	
	//Create cache
    protected LinkedHashMap<Key,Val> cache;
    
	public Val getObj(Key key) {
		return cache.get(key);
	}
    
	public void remove(Key key) {
		cache.remove(key);
	}
	
	public void add(Key key, Val val) {
		cache.put(key, val);
	}
	
}