package mvc; import java.awt.BorderLayout; import java.awt.event.ActionEvent; import java.awt.event.ActionListener; import java.beans.PropertyChangeEvent; import java.beans.PropertyChangeListener; import java.beans.PropertyChangeSupport; import java.io.File; import java.io.FilenameFilter; import javax.swing.*; /** * A directory watcher periodially checks for modification of a directory * and fires a property change event when it notices a change. * @author kjg * @version 1.0 * */ public class DirectoryWatcher implements ActionListener { PropertyChangeSupport pcs; // to manage listeners and firing events File dir; // the directory being watched long lastModified; // to remember the previously seen modification time Timer timer; // for periodic examination of the modification time /** * Creates a directory watcher for the given directory * @param dir the directory to be watched for modification */ public DirectoryWatcher(File dir) { pcs = new PropertyChangeSupport(this); timer = new Timer(500, this); // check for modification every 1/2 second setDirectory(dir); } /** * Changes the directory to be watched. Will cause a property change event to be * fired initially, and then each time the watcher notices modification of the directory. * @param dir the directory to watch, or null if no directory is to be watched * @throws IllegalArgumentException the parameter is not a directory */ public void setDirectory(File dir) { if (dir == null) timer.stop(); else if (!dir.isDirectory()) { timer.stop(); throw new IllegalArgumentException(dir + " is not a directory."); } else { this.dir = dir; lastModified = 0; timer.start(); } } /** * Gets the directory being watched * @return the currently watched directory */ public File getDirectory() { return dir; } /** * On each "tick" of the timer, check to see if the watched directory has been modified * since the last time it was checked. */ public void actionPerformed(ActionEvent ae) { if (dir.lastModified() > lastModified) { lastModified = dir.lastModified(); // remember the new modification time pcs.firePropertyChange("directory modified", null, null); } } /** * Adds a property change listener to be notified whenever the directory is modified, or * when a different directory is being watched. * @param listener */ public void addPropertyChangeListener(PropertyChangeListener listener) { pcs.addPropertyChangeListener(listener); } public void removePropertyChangeListener(PropertyChangeListener listener) { pcs.removePropertyChangeListener(listener); } // This test program uses the DirectoryWatcher. It creates a JList that is // updated whenever the contents of the selected directory changes. public static void main(String[] args) { final JFrame f = new JFrame(); f.setLayout(new BorderLayout()); // Create the JList and directory watcher. final JList list = new JList(); final DirectoryWatcher dw = new DirectoryWatcher(new File(".")); // Add a listener to the directory watcher to update the list when the directory // is modified. dw.addPropertyChangeListener(new PropertyChangeListener() { public void propertyChange(PropertyChangeEvent arg0) { list.setListData(dw.dir.list(new FilenameFilter(){ // as a filtering example, include only directories and java files in the list public boolean accept(File directory, String name) { return new File(directory,name).isDirectory() || name.contains(".java"); } })); } }); //Create a browse button and an action listener to bring up a file chooser for //changing directories. JButton b = new JButton("browse..."); b.addActionListener(new ActionListener() { public void actionPerformed(ActionEvent arg0) { JFileChooser chooser = new JFileChooser(dw.getDirectory()); chooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY); int userResponse = chooser.showOpenDialog(f); if (userResponse == JFileChooser.APPROVE_OPTION) { dw.setDirectory(chooser.getSelectedFile()); } } }); // Put the list (within a scroll pane) and the browse button into the frame f.add(new JScrollPane(list)); f.add(b, BorderLayout.SOUTH); f.pack(); f.setVisible(true); f.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); } }