/**
* Copyright (c) 2005-2011 by Appcelerator, Inc. All Rights Reserved.
* Licensed under the terms of the Eclipse Public License (EPL).
* Please see the license.txt included with this distribution for details.
* Any modifications to this file must keep this entire header intact.
*/
package org.python.pydev.core.path_watch;
import java.io.File;
import java.util.Map;
import java.util.Set;
import java.util.Map.Entry;
import name.pachler.nio.file.Path;
import name.pachler.nio.file.WatchKey;
import org.eclipse.core.runtime.Assert;
import org.python.pydev.core.ListenerList;
import org.python.pydev.core.OrderedMap;
import com.aptana.shared_core.string.FastStringBuffer;
/**
* This object will stack many ADD/REMOVE changes into a single change. It also deals with OVERFLOW changes, which
* mean that too many changes occurred and thus can't be properly mapped to the actual events. In this case,
* a notification that the base path was removed and then added again is issued (listener clients must take care
* of properly dealing with this notification, as no events of added/removed children will be issued in this case).
*
* @author fabioz
*/
public class EventsStackerRunnable implements Runnable {
public final static int ADDED = 0;
public final static int REMOVED = 1;
/**
* May be null!
*/
/*default*/volatile WatchKey key;
public final ListenerList<IFilesystemChangesListener> list;
public final Path watchedPath;
/**
* Lock for dealing with fileToEvent and overflow.
*/
private final Object lock = new Object();
/**
* The file mapping to the last event recorded in it.
*/
private Map<File, Integer> fileToEvent = new OrderedMap<File, Integer>();
/**
* The directory being watched, where the overflow occurred.
*/
private volatile File overflow = null;
/**
* Creates the events stacker based on the key, path and listeners related (the contents of the listeners may
* change later on, but the actual key and path may not change).
*/
public EventsStackerRunnable(WatchKey key, Path watchedPath, ListenerList<IFilesystemChangesListener> list) {
Assert.isNotNull(list);
Assert.isNotNull(watchedPath);
this.list = list;
this.key = key; //the key may be null!
this.watchedPath = watchedPath;
}
/**
* When run, it'll notify clients about the events that were stacked as it makes sense (i.e.: if multiple additions
* or removals of a file were issued, only the last one will actually be seen by clients).
*/
public void run() {
Map<File, Integer> currentFileToEvent;
File currentOverflow;
synchronized (lock) {
currentFileToEvent = fileToEvent;
fileToEvent = new OrderedMap<File, Integer>();
currentOverflow = overflow;
overflow = null;
}
IFilesystemChangesListener[] listeners = list.getListeners();
if (listeners.length > 0) {
if (currentOverflow != null) {
for (IFilesystemChangesListener iFilesystemChangesListener : listeners) {
//Say that the dir was removed...
File watched = new File(watchedPath.toString());
iFilesystemChangesListener.removed(watched);
if (watched.exists()) {
//And later added again (without notifying about inner contents!!)
iFilesystemChangesListener.added(watched);
}
}
return;
}
Set<Entry<File, Integer>> entrySet = currentFileToEvent.entrySet();
for (Entry<File, Integer> entry : entrySet) {
Integer value = entry.getValue();
File currKey = entry.getKey();
switch (value) {
case ADDED:
for (IFilesystemChangesListener iFilesystemChangesListener : listeners) {
iFilesystemChangesListener.added(currKey);
}
break;
case REMOVED:
for (IFilesystemChangesListener iFilesystemChangesListener : listeners) {
iFilesystemChangesListener.removed(currKey);
}
break;
}
}
}
}
/**
* On overflow we'll clear all the other events and just send the overflow (any subsequent event is
* ignored until the overflow is signaled).
*/
public void overflow(File file) {
synchronized (lock) {
this.overflow = file;
fileToEvent.clear();
}
}
public void added(File file) {
synchronized (lock) {
if (overflow == null) {
fileToEvent.put(file, ADDED);
}
}
}
public void removed(File file) {
synchronized (lock) {
if (overflow == null) {
fileToEvent.put(file, REMOVED);
}
}
}
/* (non-Javadoc)
* @see java.lang.Object#toString()
*/
@Override
public String toString() {
return new FastStringBuffer().append("EventsStackerRunnable(key=").appendObject(this.key)
.append(";watchedPath=").appendObject(this.watchedPath).append(";overflow=")
.appendObject(this.overflow).append(";fileToEvent=").appendObject(this.fileToEvent)
.append(";listeners=").appendObject(this.list.getListeners()).append(")").toString();
}
}