PageRenderTime 50ms CodeModel.GetById 38ms app.highlight 9ms RepoModel.GetById 1ms app.codeStats 0ms

/bundles/plugins-trunk/XML/xml/cache/Cache.java

#
Java | 235 lines | 162 code | 24 blank | 49 comment | 39 complexity | ffdbdc340a9d3a035ea86d8a55010115 MD5 | raw file
  1package xml.cache;
  2
  3import java.util.Set;
  4import java.util.HashSet;
  5import java.util.Iterator;
  6import java.util.List;
  7import java.util.ArrayList;
  8
  9import static org.gjt.sp.jedit.EditBus.EBHandler;
 10import org.gjt.sp.jedit.jEdit;
 11import org.gjt.sp.jedit.EditBus;
 12import org.gjt.sp.jedit.msg.BufferUpdate;
 13import org.gjt.sp.jedit.buffer.BufferAdapter;
 14import org.gjt.sp.jedit.buffer.JEditBuffer;
 15import org.gjt.sp.jedit.Buffer;
 16import org.gjt.sp.util.Log;
 17
 18import static xml.Debug.*;
 19import xml.PathUtilities;
 20
 21/**
 22 * caching policy :
 23 *    - a file-based CacheEntry is valid until opened in jEdit
 24 *    - a buffer-based CacheEntry is invalidated on change of the buffer
 25 *    - a CacheEntry is discarded once all buffers requiring it are closed
 26 *      (similar to reference-counting)
 27 * cache cleaners :
 28 *	  - listen to Buffer modifications and invalidates entries related to them
 29 *    - listen to Buffer open and invalidates file based CacheEntry
 30 *    - listen to Buffer close and invalidate buffer based CacheEntry 
 31 *      and invalidate cache entries no more required
 32 */
 33public final class Cache extends BufferAdapter
 34{
 35	private static final String DISABLE_PROP = "xml.cache.disable-cache";
 36	
 37	private static Cache instance;
 38	
 39	private Set<CacheEntry> entries;
 40	
 41	private boolean disabled;
 42	
 43	private Cache(){
 44		entries = new HashSet<CacheEntry>();
 45		disabled = jEdit.getBooleanProperty(DISABLE_PROP);
 46	}
 47	
 48	public static Cache instance()
 49	{
 50		if(instance == null)instance = new Cache();
 51		return instance;
 52	}
 53	
 54	/**
 55	 * @param	path	path of cached resource (file:/ urls are equivalent to paths)
 56	 * @param	key	unambiguously distinguish schema from completionInfo from...
 57	 */
 58	public CacheEntry put(String path, Object key, Object value){
 59		if(DEBUG_CACHE)Log.log(Log.DEBUG,Cache.class,"put("+path+","+key+","+(value == null ? null : value.getClass())+")");
 60		if(disabled){
 61			if(DEBUG_CACHE)Log.log(Log.DEBUG,Cache.class,"cache disabled !");
 62			return null;
 63		}
 64		String npath = PathUtilities.urlToPath(path);
 65		if(npath != path){
 66			if(DEBUG_CACHE)Log.log(Log.DEBUG,Cache.class," really putting "+npath);
 67			path = npath;
 68		}
 69		CacheEntry en = new CacheEntry(path,key,value);
 70		// TODO: not sure about overwritting - not overwritting for now
 71		if(entries.contains(en)){
 72			if(DEBUG_CACHE)Log.log(Log.DEBUG,Cache.class," already in cache");
 73		}else{
 74			if(DEBUG_CACHE)Log.log(Log.DEBUG,Cache.class," not in cache");
 75			entries.add(en);
 76		}
 77		return en;
 78	}
 79	
 80	/**
 81	 * @param	path	path of cached resource (file:/ urls are equivalent to paths)
 82	 * @param	key		unambiguously distinguish schema from completionInfo from...
 83	 */
 84	public CacheEntry get(String path, Object key){
 85		if(DEBUG_CACHE)Log.log(Log.DEBUG,Cache.class,"get("+path+","+key+")");
 86		if(disabled){
 87			if(DEBUG_CACHE)Log.log(Log.DEBUG,Cache.class,"cache disabled !");
 88			return null;
 89		}
 90		String npath = PathUtilities.urlToPath(path);
 91		if(npath != path){
 92			if(DEBUG_CACHE)Log.log(Log.DEBUG,Cache.class," really getting "+npath);
 93			path = npath;
 94		}
 95		for(Iterator<CacheEntry> it=entries.iterator();it.hasNext();){
 96			CacheEntry en = it.next();
 97			if(en.getPath().equals(path)
 98				&& en.getKey().equals(key)){
 99				if(DEBUG_CACHE)Log.log(Log.DEBUG,Cache.class," found");
100				return en;
101			}
102		}
103		if(DEBUG_CACHE)Log.log(Log.DEBUG,Cache.class," not found");
104		return null;
105	}
106	
107	/**
108	 * invalidate cache entries corresponding to (not requested by) this buffer
109	 */
110	public void contentInserted(JEditBuffer buffer, int startLine, int offset, int numLines, int length){
111		handleBufferContentChanged((Buffer)buffer);
112	}
113
114	/**
115	 * invalidate cache entries corresponding to (not requested by) this buffer
116	 */
117	public void contentRemoved(JEditBuffer buffer, int startLine, int offset, int numLines, int length){
118		handleBufferContentChanged((Buffer)buffer);
119	}
120
121	/**
122	 * invalidate cache entries corresponding to (not requested by) this buffer
123	 */
124	public void transactionComplete(JEditBuffer buffer){
125		handleBufferContentChanged((Buffer)buffer);
126	}
127
128	@EBHandler
129	public void handleBufferUpdate(BufferUpdate message)
130	{
131		if(BufferUpdate.CLOSED.equals(message.getWhat())){
132			if(DEBUG_CACHE)Log.log(Log.DEBUG,Cache.class,"buffer closed");
133			invalidateCacheEntriesFromPath(message.getBuffer().getPath());
134			invalidateCacheEntriesRequiredByBuffer(message.getBuffer());
135			message.getBuffer().removeBufferListener(this);
136		}else if(BufferUpdate.LOADED.equals(message.getWhat())){
137			if(DEBUG_CACHE)Log.log(Log.DEBUG,Cache.class,"buffer opened");
138			invalidateCacheEntriesFromPath(message.getBuffer().getPath());
139			message.getBuffer().addBufferListener(this);
140		}
141	}
142	
143	public void handleBufferContentChanged(Buffer buffer)
144	{
145		invalidateCacheEntriesFromPath(buffer.getPath());
146	}
147	
148	private void invalidateCacheEntriesFromPath(String path){
149		if(DEBUG_CACHE)Log.log(Log.DEBUG,Cache.class,"invalidateCacheEntriesFromPath("+path+")");
150		List<CacheEntry> toRemove = new ArrayList<CacheEntry>();
151		for(Iterator<CacheEntry> it=entries.iterator();it.hasNext();){
152			CacheEntry en = it.next();
153			if(en.getPath().equals(path)){
154				if(DEBUG_CACHE)Log.log(Log.DEBUG,Cache.class,"invalidating "+en);
155				it.remove();
156				toRemove.addAll(en.getRelated());
157			}
158		}
159		removeRelated(new HashSet<CacheEntry>(),toRemove);
160	}
161	
162	private void invalidateCacheEntriesRequiredByBuffer(Buffer b){
163		List<CacheEntry> toRemove = new ArrayList<CacheEntry>();
164		for(Iterator<CacheEntry> it=entries.iterator();it.hasNext();){
165			CacheEntry en = it.next();
166			Set<Buffer> reqB = en.getRequestingBuffers();
167			if(reqB.contains(b)){
168				reqB.remove(b);
169				if(reqB.isEmpty()){
170					if(DEBUG_CACHE)Log.log(Log.DEBUG,Cache.class,"invalidating "+en);
171					it.remove();
172					toRemove.addAll(en.getRelated());
173				}
174			}
175		}
176		removeRelated(new HashSet<CacheEntry>(), toRemove);
177	}
178	
179	/**
180	 * recursively remove related CacheEntries
181	 * @param	removed	avoid infinite recursion when related are recursive
182	 */
183	private void removeRelated(Set<CacheEntry> removed,List<CacheEntry> toRemove){
184		for(CacheEntry ce : toRemove){
185			if(!removed.contains(ce)){
186				if(DEBUG_CACHE)Log.log(Log.DEBUG,Cache.class,"invalidating related "+ce);
187				entries.remove(ce);
188				removed.add(ce);
189				removeRelated(removed, ce.getRelated());
190			}
191		}
192	}
193	
194	/**
195	 * add to EditBus
196	 */
197	public void start(){
198		EditBus.addToBus(this);
199	}
200
201	/**
202	 * clear, remove from EditBus, forget singleton
203	 */
204	public void stop(){
205		EditBus.removeFromBus(this);
206		for(Buffer b:jEdit.getBuffers()){
207			b.removeBufferListener(this);
208		}
209		entries.clear();
210		instance = null;
211	}
212	
213	/**
214	 * remove all cache entries
215	 */
216	public void clear(){
217		entries.clear();
218	}
219	
220	/**
221	 * disable caching (persistent)
222	 */
223	public void disable(){
224		disabled = true;
225		jEdit.setBooleanProperty(DISABLE_PROP,true);
226	}
227
228	/**
229	 * enable caching (persistent)
230	 */
231	public void enable(){
232		disabled = false;
233		jEdit.unsetProperty(DISABLE_PROP);
234	}
235}