package tk.amberide.ide.data.io; /* * This code is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This code is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program; if not, write to the Free * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, * MA 02111-1307, USA. */ import java.util.*; import java.io.File; import java.lang.ref.WeakReference; /** * Class for monitoring changes in disk files. Usage: * * 1. Implement the FileListener interface. 2. Create a FileMonitor instance. 3. * Add the file(s)/directory(ies) to listen for. * * fileChanged() will be called when a monitored file is created, deleted or its * modified time changes. * * @author <a href="mailto:info@geosoft.no">GeoSoft</a> */ public class FileMonitor { private Timer timer_; private HashMap<File, Long> files_; private Collection<WeakReference<FileListener>> listeners_; /** * Create a file monitor instance with specified polling interval. * * @param pollingInterval Polling interval in milli seconds. */ public FileMonitor(long pollingInterval) { files_ = new HashMap<File, Long>(); listeners_ = new ArrayList<WeakReference<FileListener>>(); timer_ = new Timer(true); timer_.schedule(new FileMonitorNotifier(), 0, pollingInterval); } /** * Stop the file monitor polling. */ public void stop() { timer_.cancel(); } /** * Add file to listen for. File may be any java.io.File (including a * directory) and may well be a non-existing file in the case where the * creating of the file is to be trepped. * <p> * More than one file can be listened for. When the specified file is * created, modified or deleted, listeners are notified. * * @param file File to listen for. */ public void addFile(File file) { if (file.isDirectory()) { for (File f : file.listFiles()) { addFile(f); } } if (!files_.containsKey(file)) { long modifiedTime = file.exists() ? file.lastModified() : -1; files_.put(file, new Long(modifiedTime)); } } /** * Remove specified file for listening. * * @param file File to remove. */ public void removeFile(File file) { files_.remove(file); } /** * Add listener to this file monitor. * * @param fileListener Listener to add. */ public void addListener(FileListener fileListener) { // Don't add if its already there for (Iterator<WeakReference<FileListener>> i = listeners_.iterator(); i.hasNext();) { WeakReference<FileListener> reference = i.next(); FileListener listener = reference.get(); if (listener == fileListener) { return; } } // Use WeakReference to avoid memory leak if this becomes the // sole reference to the object. listeners_.add(new WeakReference(fileListener)); } /** * Remove listener from this file monitor. * * @param fileListener Listener to remove. */ public void removeListener(FileListener fileListener) { for (Iterator<WeakReference<FileListener>> i = listeners_.iterator(); i.hasNext();) { WeakReference<FileListener> reference = i.next(); FileListener listener = reference.get(); if (listener == fileListener) { i.remove(); break; } } } /** * This is the timer thread which is executed every n milliseconds according * to the setting of the file monitor. It investigates the file in question * and notify listeners if changed. */ private class FileMonitorNotifier extends TimerTask { public void run() { // Loop over the registered files and see which have changed. // Use a copy of the list in case listener wants to alter the // list within its fileChanged method. Collection<File> files = new ArrayList<File>(files_.keySet()); for (Iterator i = files.iterator(); i.hasNext();) { File file = (File) i.next(); long lastModifiedTime = files_.get(file); long newModifiedTime = file.exists() ? file.lastModified() : -1; // Chek if file has changed if (newModifiedTime != lastModifiedTime) { // Register new modified time files_.put(file, newModifiedTime); // Notify listeners for (Iterator<WeakReference<FileListener>> j = listeners_.iterator(); j.hasNext();) { WeakReference<FileListener> reference = j.next(); FileListener listener = reference.get(); // Remove from list if the back-end object has been GC'd if (listener == null) { j.remove(); } else { listener.fileChanged(file); } } } } } } /** * Interface for listening to disk file changes. * * @see FileMonitor * * @author <a href="mailto:info@geosoft.no">GeoSoft</a> */ public interface FileListener { /** * Called when one of the monitored files are created, deleted or * modified. * * @param file File which has been changed. */ void fileChanged(File file); } }