All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javaxt.io.Directory Maven / Gradle / Ivy

package javaxt.io;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.LinkedList;
import java.util.List;
import java.util.Random;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;

import de.mhus.lib.core.MLog;

//******************************************************************************
//**  Directory Class - By Peter Borissow
//******************************************************************************
/**
 * Used to represent a directory on a file system. In many ways, this class is
 * an extension of the java.io.File class. However, unlike the java.io.File
 * class, this object provides functions that are relevant and specific to
 * directories. For example, this class provides a mechanism to move and copy
 * directories - something not offered by the java.io.File class. In addition,
 * this class provides a mechanism to retrieve files and folders found in a
 * directory AND any subdirectories. This is accomplished via a multi-threaded
 * recursive search. Finally, this class provides a powerful tool to monitor
 * changes made to the directory (e.g. getEvents).
 *
 ******************************************************************************/

public class Directory implements Comparable {

	private java.io.File file; // <--DO NOT USE DIRECTLY! Use getFile() instead.
	private String name = "";
	private String path = "";

	// private boolean useCache = false;
	private FileSystemWatcher FileSystemWatcher;
	private File.FileAttributes attr;
	private long lastAttrUpdate = 0;

	public static final String PathSeparator = System.getProperty("file.separator");
	protected static final boolean isWindows = System.getProperty("os.name").toLowerCase().startsWith("windows");

	// **************************************************************************
	// ** Constructor
	// **************************************************************************
	/** Creates a new instance of Directory using a path to a directory. */

	public Directory(String Path) {

		if (Path == null)
			throw new NullPointerException();

		if (Path.startsWith("\"") && Path.endsWith("\"")) {
			Path = Path.substring(1, Path.length() - 1);
		}

		init(Path);
	}

	// **************************************************************************
	// ** Constructor
	// **************************************************************************
	/** Creates a new instance of Directory using a java.io.File */

	public Directory(java.io.File File) {
		init(File);
	}

	// **************************************************************************
	// ** init
	// **************************************************************************

	private void init(java.io.File File) {

		if (File == null)
			throw new NullPointerException();

		if (File.exists() && !File.isDirectory()) {
			File = File.getParentFile();
		}

		attr = null;
		this.file = File;
		init(file.getAbsolutePath());
	}

	// **************************************************************************
	// ** init
	// **************************************************************************
	/**
	 * Used to parse the absolute path to the directory. For performance
	 * reasons, we do not rely on the java.io.File to determine the path and
	 * name variables.
	 */
	private void init(String Path) {
		Path = Path.replace("\\", "/");
		String[] arr = Path.split("/");
		if (arr.length > 1) {
			name = arr[arr.length - 1];
			path = Path.substring(0, Path.lastIndexOf(name));
		} else {
			path = Path;
		}

		if (path.isEmpty())
			path = PathSeparator;
		else
			path = path.replace("/", PathSeparator);

		if (!path.endsWith(PathSeparator))
			path += PathSeparator;
	}

	// **************************************************************************
	// ** getFile
	// **************************************************************************
	/** Returns the java.io.File representation of this object. */

	private java.io.File getFile() {
		if (file == null)
			file = new java.io.File(path + name);
		return file;
	}

	// **************************************************************************
	// ** getRootDirectories
	// **************************************************************************
	/**
	 * Returns an array of root directories on the filesystem (e.g. "/" or
	 * C:\"). On Windows, this method will return all mounted drives, including
	 * any that might have been disconnected. The array will be empty if there
	 * are no root directories or if the set of roots could not be determined.
	 */
	public static Directory[] getRootDirectories() {

		java.util.TreeSet directories = new java.util.TreeSet();
		java.io.File[] files = java.io.File.listRoots();
		if (files == null)
			return new Directory[0];
		for (int i = 0; i < files.length; i++) {
			directories.add(new Directory(files[i]));
		}

		// Pick up any windows mounted drives that might not have been returned
		// from the listRoots() method (e.g. disconnected mounted drives).
		if (isWindows) {

			boolean doNetUse = false;
			if (File.loadDLL()) {
				try {
					String drives = File.GetNetworkDrives();
					if (drives != null) {
						for (String drive : drives.split("\n")) {
							if (drive.trim().length() > 0) {
								String[] arr = drive.split("\t");
								String driveName = arr[0];
								String driveType = arr[1];
								directories.add(new Directory(driveName));
							}
						}
					}
				} catch (Exception e) {
					doNetUse = true;
				}
			} else {
				doNetUse = true;
			}

			if (doNetUse) {
				javaxt.io.Shell cmd = new javaxt.io.Shell("net use");
				cmd.run();

				java.util.Iterator it = cmd.getOutput().iterator();
				boolean parse = false;
				while (it.hasNext()) {
					String line = it.next();
					if (line == null)
						break;
					else
						line = line.trim();
					if (!parse && line.startsWith("----")) {
						parse = true;
						line = it.next();
						if (line == null)
							break;
					}
					if (line.contains(":")) {
						line = line.substring(0, line.indexOf(":") + 1);
						line = line.substring(line.lastIndexOf(" ") + 1);
						directories.add(new Directory(line.trim()));
					}
				}
			}
		}

		return directories.toArray(new Directory[directories.size()]);
	}

	// **************************************************************************
	// ** Exists
	// **************************************************************************
	/** Used to determine whether a directory exists on the file system. */

	public boolean exists() {

		String path = this.path + name;

		// Special case for a directory whose path represents a server name
		// (e.g. "\\192.168.0.1")
		if (isWindows && path.startsWith("\\\\")) {
			if (path.endsWith(PathSeparator))
				path = path.substring(0, path.length() - 1);
			if (!path.substring(2).contains(PathSeparator)) {
				boolean doNetUse = false;
				if (File.loadDLL()) {
					try {
						File.GetSharedDrives(path.substring(2));
						return true;
					} catch (Exception e) {
						if (e.getMessage().trim().equals("53")) {
							return false;
						} else {
							doNetUse = true;
						}
					}
				} else {
					doNetUse = true;
				}

				if (doNetUse) {
					javaxt.io.Shell cmd = new javaxt.io.Shell("net view " + path);
					cmd.run();
					java.util.List errors = cmd.getErrors();
					errors.remove(null);
					if (errors.isEmpty()) {
						return true;
					} else {
						return false;
					}
				}

			}
		}

		if (file != null) {
			return (file.isDirectory() && file.exists());
		}

		try {
			File.FileAttributes attr = getFileAttributes();
			if (attr != null)
				return true;
			else {
				getFile();
				return (file.isDirectory() && file.exists());
			}
		} catch (java.io.FileNotFoundException e) {
			return false;
		}
	}

	// **************************************************************************
	// ** isEmpty
	// **************************************************************************
	/**
	 * Used to determine whether the directory is empty. Returns true if no
	 * files or directories are present in the current directory.
	 */
	public boolean isEmpty() {
		if (!exists())
			return true;
		Object[] files = listFiles(null);
		return (files == null);
	}

	// **************************************************************************
	// ** Create Directory
	// **************************************************************************
	/** Used to create the directory. */

	public boolean create() {
		return getFile().mkdirs();
	}

	// **************************************************************************
	// ** Delete Directory
	// **************************************************************************
	/** Used to delete the directory. */

	public boolean delete() {
		if (getFile().delete()) {
			attr = null;
			return true;
		} else {
			return false;
		}
	}

	// **************************************************************************
	// ** Copy To
	// **************************************************************************
	/**
	 * Used to copy a directory to another directory. Preserves the last
	 * modified date associated with the source files and directories. Returns a
	 * list of any files that failed to copy.
	 */
	public String[] copyTo(Directory Destination, boolean Overwrite) {
		return copyTo(Destination, null, Overwrite);
	}

	// **************************************************************************
	// ** Copy To
	// **************************************************************************
	/**
	 * Used to copy a directory to another directory. Provides a filter option
	 * to copy specific files (e.g. "*.jpg"). Preserves the last modified date
	 * associated with the source files and directories. Returns a list of any
	 * files that failed to copy.
	 *
	 * @param filter
	 *            A file filter. You can pass in a java.io.FileFilter, a String
	 *            (e.g. "*.txt"), or an array of Strings (e.g. String[]{"*.txt",
	 *            "*.doc"}). Wildcard filters are supported. Note that the
	 *            filter is only applied to files, not directories.
	 *
	 * @param Overwrite
	 *            If true, overwrites any existing files/directories
	 */
	public String[] copyTo(Directory Destination, Object filter, boolean Overwrite) {

		int source = toString().length();
		String destination = Destination.toString();
		java.util.ArrayList failures = new java.util.ArrayList();

		// Create new folder
		if (!Destination.exists())
			Destination.create();

		// Initiate search
		java.util.List results = this.getChildren(true, filter, false);
		while (true) {

			Object item;
			synchronized (results) {

				// Wait for files/directories to be added to the list
				while (results.isEmpty()) {
					try {
						results.wait();
					} catch (InterruptedException e) {
						break;
					}
				}

				// Grab the next available file/directory from the list. Note
				// that
				// we are INSIDE the synchronized block! This forces the
				// directory
				// search to insert only one record at a time. The primary
				// motivation
				// for this is to ensure that we don't run out of memory when
				// copying a large number of files/directories.
				item = results.get(0);
				if (item != null) {

					if (item instanceof javaxt.io.File) {

						javaxt.io.File file = (javaxt.io.File) item;
						String FilePath = file.toString();
						File out = new File(destination + FilePath.substring(source));

						boolean success = file.copyTo(out, Overwrite);
						if (!success)
							failures.add(FilePath);
					} else {
						javaxt.io.Directory dir = (javaxt.io.Directory) item;
						new Directory(destination + dir.toString().substring(source)).create();
					}

				} else {
					break;
				}

				results.remove(0);
				results.notifyAll();
			}
		}

		// Return list of failed copies
		return failures.toArray(new String[failures.size()]);
	}

	// **************************************************************************
	// ** Move To
	// **************************************************************************
	/** Used to move a directory from one directory to another. */

	public void moveTo(Directory Destination, boolean Overwrite) {
		if (Overwrite || !Destination.exists()) {
			java.io.File newFile = Destination.toFile();
			if (getFile().renameTo(newFile)) {
				init(newFile);
			}
		}
	}

	// **************************************************************************
	// ** Rename
	// **************************************************************************
	/** Used to rename the directory. */

	public void rename(String Name) {
		java.io.File newFile = new java.io.File(path + Name);
		if (getFile().renameTo(newFile)) {
			init(newFile);
		}
	}

	// **************************************************************************
	// ** getName
	// **************************************************************************
	/** Returns the name of the directory (excluding the path). */

	public String getName() {
		return name;
	}

	// **************************************************************************
	// ** getPath
	// **************************************************************************
	/**
	 * Returns the full path to this directory, including the directory name.
	 * The path includes a system-dependent default name-separator character at
	 * the end of the string (e.g. "/" or "\").
	 */
	public String getPath() {
		String path = this.path + name;
		if (path.endsWith(PathSeparator))
			return path;
		else
			return path + PathSeparator;
	}

	// **************************************************************************
	// ** getDate
	// **************************************************************************
	/**
	 * Returns a timestamp of when the directory was last modified. This is
	 * identical to the getLastModifiedTime() method.
	 */
	public java.util.Date getDate() {
		if (file != null)
			return getFileDate();

		try {
			File.FileAttributes attr = getFileAttributes();
			if (attr != null)
				return attr.getLastWriteTime();
			else {
				getFile();
				return getFileDate();
			}
		} catch (java.io.FileNotFoundException e) {
			return null;
		}

	}

	private java.util.Date getFileDate() {
		if (file.exists() && file.isDirectory())
			return new java.util.Date(file.lastModified());
		else
			return null;
	}

	// **************************************************************************
	// ** setDate
	// **************************************************************************
	/**
	 * Used to set the timestamp of when the directory was last modified.
	 */
	public boolean setDate(java.util.Date lastModified) {
		if (lastModified != null) {
			java.io.File file = getFile();
			if (file.exists() && file.isDirectory()) {
				long t = lastModified.getTime();
				if (getDate().getTime() != t) {
					attr = null;
					return getFile().setLastModified(t);
				}
			}
		}
		return false;
	}

	// //**************************************************************************
	// //** getSize
	// //**************************************************************************
	// /** Used to retrieve the size of the directory object, in bytes. Note
	// that
	// * the getContentSize() method will return the total size of all the files
	// * and folders found in this directory.
	// */
	// public long getSize(){
	// if (file!=null) return getFileSize();
	//
	// try{
	// File.FileAttributes attr = getFileAttributes();
	// if (attr!=null) return attr.getSize();
	// else{
	// getFile();
	// return getFileSize();
	// }
	// }
	// catch(java.io.FileNotFoundException e){
	// return 0L;
	// }
	// }
	//
	// private long getFileSize(){
	// long size = file.length();
	// if (size>0L && file.isDirectory()) return size;
	// else return 0L;
	// }

	// **************************************************************************
	// ** getSize
	// **************************************************************************
	/**
	 * Returns the total size of all the files and folders found in this
	 * directory, in bytes.
	 */
	public long getSize() {

		long size = 0L;

		// Initiate search
		java.util.List results = getChildren(true, null, false);

		while (true) {
			Object item;
			synchronized (results) {
				while (results.isEmpty()) {
					try {
						results.wait();
					} catch (InterruptedException e) {
						break;
					}
				}
				item = results.remove(0);
				results.notifyAll();
			}

			if (item != null) { // Do something with the item.

				if (item instanceof javaxt.io.File) {
					javaxt.io.File file = (javaxt.io.File) item;
					size += file.getSize();
				} else {
					javaxt.io.Directory dir = (javaxt.io.Directory) item;
					try {
						// size += dir.getFileAttributes().getSize();
					} catch (Exception e) {
					}
				}
			} else { // item is null. This is our queue that the search is done!
				return size;
			}
		}
	}

	// **************************************************************************
	// ** isHidden
	// **************************************************************************
	/** Used to determine whether this Directory is Hidden */

	public boolean isHidden() {

		if (file != null) {
			return (file.isDirectory() && file.isHidden());
		}

		try {
			File.FileAttributes attr = getFileAttributes();
			if (attr != null)
				return attr.isHidden();
			else {
				getFile();
				return (file.isDirectory() && file.isHidden());
			}
		} catch (java.io.FileNotFoundException e) {
			return false;
		}
	}

	// **************************************************************************
	// ** isLink
	// **************************************************************************
	/**
	 * Used to determine whether the directory is actually a link to another
	 * file or directory. Returns true for symbolic links and Windows junctions.
	 */
	public boolean isLink() {
		return getLink() != null;
	}

	// **************************************************************************
	// ** getLink
	// **************************************************************************
	/**
	 * Returns the target of a symbolic link or Windows junction
	 */
	public java.io.File getLink() {
		try {
			return getFileAttributes().getLink();
		} catch (Exception e) {
			return null;
		}
	}

	// **************************************************************************
	// ** getLastModifiedTime
	// **************************************************************************
	/**
	 * Returns a timestamp of when the directory was last modified. This is
	 * identical to the getDate() method.
	 */
	public java.util.Date getLastModifiedTime() {
		return this.getDate();
	}

	// **************************************************************************
	// ** getCreationTime
	// **************************************************************************
	/**
	 * Returns a timestamp of when the directory was first created. Returns a
	 * null if the timestamp is not available.
	 */
	public java.util.Date getCreationTime() {
		try {
			return getFileAttributes().getCreationTime();
		} catch (Exception e) {
			return null;
		}
	}

	// **************************************************************************
	// ** getLastAccessTime
	// **************************************************************************
	/**
	 * Returns a timestamp of when the directory was last accessed. Returns a
	 * null if the timestamp is not available.
	 */
	public java.util.Date getLastAccessTime() {
		try {
			return getFileAttributes().getLastAccessTime();
		} catch (Exception e) {
			return null;
		}
	}

	// **************************************************************************
	// ** getFlags
	// **************************************************************************
	/**
	 * Returns keywords representing directory attributes. Returns an empty
	 * HashSet if the attributes are not available.
	 */
	public java.util.HashSet getFlags() {
		try {
			return getFileAttributes().getFlags();
		} catch (Exception e) {
			return new java.util.HashSet();
		}
	}

	// **************************************************************************
	// ** getFileAttributes
	// **************************************************************************
	/**
	 * Returns file attributes such as when the file was first created and when
	 * it was last accessed. File attributes are cached for up to one second.
	 * This provides users the ability to retrieve multiple attributes at once.
	 * Without caching, we would have to ping the file system every time we call
	 * getLastAccessTime(), getLastAccessTime(), getLastWriteTime(), etc. The
	 * cached attributes are automatically updated when the file is updated or
	 * deleted by this class.
	 */
	public File.FileAttributes getFileAttributes() throws java.io.FileNotFoundException {

		if (attr == null)
			lastAttrUpdate = 0;

		if (attr == null || (new java.util.Date().getTime() - lastAttrUpdate) > 1000) {
			try {
				// Get directory attributes
				String pathToFile = toString();
				attr = new File.FileAttributes(pathToFile);
				if (!attr.isDirectory())
					throw new java.io.FileNotFoundException(pathToFile);

				// Set lastUpdate (used to cache file attributes)
				lastAttrUpdate = new java.util.Date().getTime();
			} catch (java.io.FileNotFoundException e) {
				attr = null;
				throw e;
			} catch (Exception e) {
				// e.printStackTrace();
				attr = null;
			}
		}
		return attr;
	}

	// **************************************************************************
	// ** getParentDirectory
	// **************************************************************************
	/**
	 * Used to retrieve this Directory's Parent. Returns null if there is no
	 * parent directory.
	 */
	public Directory getParentDirectory() {
		if (name.isEmpty()) {
			return null;
		} else {
			return new Directory(path);
		}
	}

	// **************************************************************************
	// ** toFile
	// **************************************************************************
	/** Used to retrieve the java.io.File representation by this object. */

	public java.io.File toFile() {
		return getFile();
	}

	// **************************************************************************
	// ** getFiles
	// **************************************************************************
	/**
	 * Used to retrieve an array of files found in this directory. Returns an
	 * empty array if no files are found.
	 *
	 * @param filter
	 *            A file filter. You can pass in a java.io.FileFilter, a String
	 *            (e.g. "*.txt"), or an array of Strings (e.g. String[]{"*.txt",
	 *            "*.doc"}). Wildcard filters are supported. Note that the
	 *            filter is only applied to files, not directories.
	 */
	public File[] getFiles(Object filter) {

		if (this.exists()) {

			FileFilter fileFilter = new FileFilter(filter);
			Object[] files = listFiles(fileFilter);
			if (files == null) {
				return new File[0];
			} else {

				java.util.ArrayList list = new java.util.ArrayList();
				for (int i = 0; i < files.length; i++) {
					java.io.File file = null;
					if (files[i] instanceof java.io.File) {
						file = (java.io.File) files[i];
					} else {
						file = new java.io.File(files[i].toString());
					}
					if (file.exists() && file.isFile()) {
						list.add(new javaxt.io.File(file));
					}
				}
				Collections.sort(list, new FileComparer(this));
				return list.toArray(new javaxt.io.File[list.size()]);
			}
		} else {
			return new File[0];
		}
	}

	// **************************************************************************
	// ** getFiles
	// **************************************************************************
	/**
	 * Used to retrieve an array of files found in this directory. Returns an
	 * empty array if no files are found.
	 */
	public File[] getFiles() {
		return getFiles(null);
	}

	// **************************************************************************
	// ** getFiles
	// **************************************************************************
	/**
	 * Used to retrieve an array of files found in this directory. Returns an
	 * empty array if no files are found.
	 *
	 * @param filter
	 *            A file filter. You can pass in a java.io.FileFilter, a String
	 *            (e.g. "*.txt"), or an array of Strings (e.g. String[]{"*.txt",
	 *            "*.doc"}). Wildcard filters are supported. Note that the
	 *            filter is only applied to files, not directories.
	 *
	 * @param RecursiveSearch
	 *            If true, will perform a multi-threaded, recursive directory
	 *            search to find all the files found in the current directory,
	 *            including any subdirectories. If false, the method will simply
	 *            return files found in the current directory. 
* * Note that if the thread is interrupted for whatever reason * during a recursive search, the search will stop immediately. * Consequently, the returned array may be incomplete. You can * check the interrupted status with the Thread.isInterrupted() * method. Alternatively, you can read and clear the interrupted * status in a single operation using the Thread.interrupted() * method. */ public File[] getFiles(Object filter, boolean RecursiveSearch) { if (this.exists()) { if (RecursiveSearch) { // Get list of all the files and folders in the directory List items = getChildren(true, filter, false); ArrayList files = new ArrayList(); try { Object item; while (true) { synchronized (items) { while (items.isEmpty()) { items.wait(); } item = items.remove(0); items.notifyAll(); } if (item == null) { break; } else { if (item instanceof File) { files.add((File) item); } } } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } // Sort the list Collections.sort(files, new FileComparer(this)); // Convert the list into an array of files return files.toArray(new File[files.size()]); } else { return getFiles(filter); } } else { return new File[0]; } } // ************************************************************************** // ** getFiles // ************************************************************************** /** * Used to retrieve an array of files found in this directory. * * @param RecursiveSearch * If true, will perform a multi-threaded, recursive directory * search to find all the files found in the current directory, * including any subdirectories. If false, the method will simply * return files found in the current directory.
* * Note that if the thread is interrupted for whatever reason * during a recursive search, the search will stop immediately. * Consequently, the returned array may be incomplete. You can * check the interrupted status with the Thread.isInterrupted() * method. Alternatively, you can read and clear the interrupted * status in a single operation using the Thread.interrupted() * method. */ public File[] getFiles(boolean RecursiveSearch) { return getFiles(null, RecursiveSearch); } // ************************************************************************** // ** getSubDirectories // ************************************************************************** /** * Used to retrieve an array of directories found in this directory. */ public Directory[] getSubDirectories() { return getSubDirectories(false); } // ************************************************************************** // ** getSubDirectories // ************************************************************************** /** * Used to retrieve an array of directories found in this directory. * * @param RecursiveSearch * If true, will perform a multi-threaded, recursive directory * search to find all the directories found in the current * directory, including any subdirectories. If false, the method * will simply return directories found in the current directory. *
* * Note that if the thread is interrupted for whatever reason * during a recursive search, the search will stop immediately. * Consequently, the returned array may be incomplete. You can * check the interrupted status with the Thread.isInterrupted() * method. Alternatively, you can read and clear the interrupted * status in a single operation using the Thread.interrupted() * method. */ public Directory[] getSubDirectories(boolean RecursiveSearch) { if (this.exists()) { if (RecursiveSearch) { // Get list of all the files and folders in the directory List items = getChildren(true, null, false); ArrayList directories = new ArrayList(); try { Object item; while (true) { synchronized (items) { while (items.isEmpty()) { items.wait(); } item = items.remove(0); items.notifyAll(); } if (item == null) { break; } else { if (item instanceof Directory) { directories.add((Directory) item); } } } } catch (InterruptedException e) { Thread.currentThread().interrupt(); } return directories.toArray(new Directory[directories.size()]); } else { java.io.FileFilter fileFilter = new java.io.FileFilter() { @Override public boolean accept(java.io.File file) { if (file.isDirectory()) { return true; } else { return false; } } }; Object[] files = listFiles(new FileFilter(fileFilter)); if (files == null) return new Directory[0]; else { Directory[] dirs = new Directory[files.length]; for (int i = 0; i < files.length; i++) { dirs[i] = new Directory(files[i].toString()); } return dirs; } } } else { return new Directory[0]; } } // ************************************************************************** // ** getChildren // ************************************************************************** /** * Used to retrieve an array of both files and folders found in this * directory. */ public List getChildren() { return getChildren(false); } // ************************************************************************** // ** getChildren // ************************************************************************** /** * Used to retrieve an array of both files and folders found in this * directory. */ public List getChildren(boolean RecursiveSearch) { return getChildren(RecursiveSearch, null, true); } // ************************************************************************** // ** getChildren // ************************************************************************** /** * Used to retrieve an list of files and folders found in this directory. * * @param RecursiveSearch * If true, will include files found in the subdirectories. * * @param filter * A file filter. You can pass in a java.io.FileFilter, a String * (e.g. "*.txt"), or an array of Strings (e.g. String[]{"*.txt", * "*.doc"}). Wildcard filters are supported. Note that the * filter is only applied to files, not directories. * */ public List getChildren(boolean RecursiveSearch, Object filter) { return getChildren(RecursiveSearch, filter, true); } // ************************************************************************** // ** getChildren // ************************************************************************** /** * Used to retrieve an list of files and folders found in this directory. * * @param RecursiveSearch * If true, will perform a multi-threaded, recursive directory * search to find all the files and folders found in the current * directory, including any subdirectories. If false, the method * will simply return items found in the current directory.
* * Note that if the thread is interrupted for whatever reason * during a recursive search, the search will stop immediately. * Consequently, the returned array may be incomplete. You can * check the interrupted status with the Thread.isInterrupted() * method. Alternatively, you can read and clear the interrupted * status in a single operation using the Thread.interrupted() * method. * * @param filter * A file filter. You can pass in a java.io.FileFilter, a String * (e.g. "*.txt"), or an array of Strings (e.g. String[]{"*.txt", * "*.doc"}). Wildcard filters are supported. Note that the * filter is only applied to files, not directories. * * @param wait * Used to indicate whether to wait for the list to be fully * populated before returning the response. When traversing very * a large set of files, it maybe a good idea to process the * files as they get added to the list. In this case you should * set this parameter to false. If this parameter is set to * false, a null entry will be added to the end of the list to * indicate that the directory search is complete. * * Example: * *
	 *            boolean wait = false;
	 *            java.util.List files = directory.getChildren(true, null, wait);
	 *            if (wait) {
	 *                for (int i = 0; i < files.size(); i++) {
	 *                    System.out.println(files.get(i));
	 *                }
	 *            } else {
	 *                Object obj;
	 *                while (true) {
	 *                    synchronized (files) {
	 *                        while (files.isEmpty()) {
	 *                            try {
	 *                                files.wait();
	 *                            } catch (InterruptedException e) {
	 *                                break;
	 *                            }
	 *                        }
	 *                        obj = files.remove(0);
	 *                        files.notifyAll();
	 *                    }
	 * 
	 *                    if (obj == null) {
	 *                        break;
	 *                    } else {
	 *                        System.out.println(obj);
	 *                    }
	 *                }
	 *            }
	 *            
* */ public List getChildren(boolean RecursiveSearch, Object filter, boolean wait) { if (this.exists()) { if (RecursiveSearch) { // Create list to store items found in the directory List items = new LinkedList(); // Create a file filter FileFilter fileFilter = new FileFilter(filter); // Spawn threads used to crawl through the file system long directoryID = Long .valueOf(java.util.Calendar.getInstance().getTimeInMillis() + "" + new Random().nextInt(100000)) .longValue(); int numThreads = 20; // <-- this should be set dynamically and // self tuning DirectorySearch.deleteCache(); DirectorySearch search = new DirectorySearch(fileFilter, items, directoryID, numThreads); for (int i = 0; i < numThreads; i++) { Thread t = new Thread(search); t.setName("DirectorySearch_" + directoryID + "-" + i); t.start(); } // Initiate search DirectorySearch.updatePool(this); if (wait) { synchronized (items) { while (!items.contains(null)) { try { items.wait(); } catch (InterruptedException e) { DirectorySearch.stop(); Thread.currentThread().interrupt(); return items; } } items.remove(null); items.notifyAll(); } Collections.sort(items, new FileComparer(this)); } // Return list return items; } // end recursive search else { List results = new LinkedList(); Object[] files = listFiles(new FileFilter(filter)); if (files == null) { if (!wait) results.add(null); return results; } else { for (int i = 0; i < files.length; i++) { // System.out.println(files[i]); boolean isDirectory = false; java.io.File file = null; if (files[i] instanceof java.io.File) { file = (java.io.File) files[i]; isDirectory = file.isDirectory(); } else { file = new java.io.File(files[i].toString()); isDirectory = files[i].toString().replace("\\", "/").endsWith("/"); // <-- // Need // this // check // for // windows // sym // links... } if (isDirectory) { results.add(new Directory(file)); } else { results.add(new File(file)); } } Collections.sort(results, new FileComparer(this)); if (!wait) results.add(null); return results; } } } else { // Directory does not exist, return an empty list List list = new LinkedList(); if (!wait) list.add(null); return list; } } // ************************************************************************** // ** getSharedDrives // ************************************************************************** /** Returns a list of shared directories found on a given server. */ private java.io.File[] getSharedDrives(String serverName) { if (isWindows) { serverName = serverName.replace("/", "\\\\"); if (serverName.startsWith("\\\\")) serverName = serverName.substring(2); if (serverName.contains("\\")) serverName = serverName.substring(0, serverName.indexOf("\\")); boolean doNetView = false; if (File.loadDLL()) { try { String drives = File.GetSharedDrives(serverName); if (drives != null) { java.util.ArrayList files = new java.util.ArrayList(); for (String drive : drives.split("\n")) { drive = drive.trim(); if (drive.length() > 0) { String[] arr = drive.split("\t"); files.add(new java.io.File("\\\\" + serverName + "\\" + arr[0])); } } if (files.isEmpty()) return null; else return files.toArray(new java.io.File[files.size()]); } } catch (Exception e) { doNetView = true; } } else { doNetView = true; } // If we're still here, something went wrong with the JNI. Try using // net view instead. Note that unlike the JNI, net view won't return // hidden network drives. if (doNetView) { javaxt.io.Shell cmd = new javaxt.io.Shell("net view " + serverName); cmd.run(); java.util.List errors = cmd.getErrors(); errors.remove(null); if (errors.isEmpty()) { String path = "\\\\" + serverName + "\\"; java.util.List output = cmd.getOutput(); // Remove empty lines from the standard output stream java.util.List tmp = new java.util.Vector(); java.util.Iterator it = output.iterator(); while (it.hasNext()) { String row = it.next(); if (row == null || row.trim().length() == 0) { } else tmp.add(row); } output = tmp; tmp = null; // Parse the standard output stream and create a list of // shared directories java.util.ArrayList files = new java.util.ArrayList(); int x = 0; int len = -1; it = output.iterator(); while (it.hasNext()) { String row = it.next(); if (row.startsWith("---")) { String colHeader = (String) output.get(x - 1); if (colHeader.startsWith("Share name") && colHeader.contains("Type")) { len = colHeader.indexOf("Type"); } } else { if (row.startsWith("The command completed successfully.")) { break; } if (len > 0 && row.length() > len) { String type = row.substring(len); type = type.substring(0, type.indexOf(" ")); if (type.equalsIgnoreCase("Disk")) { files.add(new java.io.File(path + row.substring(0, len).trim())); } } } x++; } // Convert the list of files into an array if (files.isEmpty()) return null; else return files.toArray(new java.io.File[files.size()]); } } } return null; } // ************************************************************************** // ** listFiles // ************************************************************************** /** Used to return a list of files found in this directory. */ protected Object[] listFiles() { return listFiles(null); } // ************************************************************************** // ** listFiles // ************************************************************************** /** * Used to return a list of files found in this directory. * * @param filter * A file filter. You can pass in a java.io.FileFilter, a String * (e.g. "*.txt"), or an array of Strings (e.g. String[]{"*.txt", * "*.doc"}). Wildcard filters are supported. Note that the * filter is only applied to files, not directories. * * @return An array of java.io.File or an array of Strings representing * paths to files. If the input FileFilter is generated using a * java.io.FileFilter, the method will return an array of * java.io.File. Otherwise, this method will return an array of * Strings for most cases. Note that any subdirectories that are * found in this directory are ALWAYS included in the result, * regardless of file filter. */ private Object[] listFiles(Object filter) { FileFilter fileFilter; if (filter instanceof FileFilter) fileFilter = (FileFilter) filter; else fileFilter = new FileFilter(filter); String path = this.path + name; // Get a list of shared drives on a windows server (e.g. // "\\192.168.0.80") if (isWindows && path.startsWith("\\\\")) { if (path.endsWith(PathSeparator)) path = path.substring(0, path.length() - 1); if (!path.substring(2).contains(PathSeparator)) { java.io.File[] sharedDrives = getSharedDrives(path); if (sharedDrives != null) { java.util.ArrayList files = new java.util.ArrayList(); for (java.io.File file : sharedDrives) { if (file.exists()) { if (fileFilter == null) { files.add(file); } else { if (fileFilter.accept(file)) { files.add(file); } } } } return files.toArray(new java.io.File[files.size()]); } return null; } } // Generate a list of files in this directory that match the file filter java.util.List files = new java.util.ArrayList(); if (isWindows) { path = getPath(); String[] list = dir(); for (int i = 0; i < list.length; i++) { boolean isDirectory = list[i].endsWith("\\"); if (fileFilter == null || isDirectory) { files.add(path + list[i]); } else { if (fileFilter.usesIOFilter()) { java.io.File file = new java.io.File(path + list[i]); if (fileFilter.accept(file)) { files.add(file); } } else { if (fileFilter.accept(list[i])) { files.add(path + list[i]); } } } } } else { // UNIX java.io.File[] fs = getFile().listFiles(); if (fs != null) { for (int i = 0; i < fs.length; i++) { java.io.File file = fs[i]; if (fileFilter == null) { files.add(file); } else { if (fileFilter.accept(file)) { files.add(file); } } } } } if (files.size() < 1) return null; // Sort the list and return an array Collections.sort(files, new FileComparer(null)); return files.toArray(new Object[files.size()]); } // ************************************************************************** // ** ls // ************************************************************************** /** * Used to list contents of a directory using a unix ls command. * * private String[] ls(){ * * * java.util.List files = new java.util.ArrayList(); try{ * String path = this.getPath(); * * * //Execute a ls command to get a directory listing String[] params = new * String[]{"ls", "-ap", path}; javaxt.io.Shell cmd = new * javaxt.io.Shell(params); java.util.List output = cmd.getOutput(); * cmd.run(true); * * * * //Parse the output String line; while (true){ synchronized (output) { * while (output.isEmpty()) { output.wait(); } line = output.remove(0); } if * (line!=null){ line = line.trim(); if (line.length()>0){ * //System.out.println(line); //boolean isDirectory = (line.endsWith("/")); * * if (!line.equals("./") && !line.equals("../")){ files.add(line); } } } * else{ break; } } } //Catch Exceptions thrown by cmd.run() * catch(java.io.IOException e){ } catch(InterruptedException e){ * Thread.currentThread().interrupt(); } * * * //Convert the Vector to an Array String[] arr = new String[files.size()]; * for (int i=0; i files = new java.util.ArrayList(); // Try listing files using the JNI boolean doDir = false; if (File.loadDLL()) { try { String list = File.GetFiles(getPath() + "*"); if (list != null) { for (String name : list.split("\n")) { name = name.trim(); if (name.length() > 0 && !(name.equals(".\\") || name.equals("..\\"))) files.add(name); } } } catch (Exception e) { doDir = true; } } else { doDir = true; } // If we're still here, list files using a command prompt if (doDir) try { String path = this.getPath(); if (path.contains(" ")) path = "\"" + path + "\""; // Execute a windows shell command to get a directory listing javaxt.io.Shell cmd = new javaxt.io.Shell("cmd.exe /c dir /OG " + path); java.util.List output = cmd.getOutput(); cmd.run(true); // Parse files returned from the directory listing boolean parseFiles = false; int colWidth = -1; String line; while (true) { synchronized (output) { while (output.isEmpty()) { output.wait(); } line = output.remove(0); } if (line != null) { if (line.length() == 0 || line.startsWith(" ")) { if (parseFiles == true) parseFiles = false; } else { if (parseFiles == false) parseFiles = true; if (parseFiles) { if (colWidth < 0) { int offset = 20; // String date = line.substring(0, 20); // Get File Type String type = line.substring(offset); if (type.trim().startsWith("<")) { offset += type.indexOf(">") + 1; type = type.substring(type.indexOf("<"), type.indexOf(">") + 1); } else { type = ""; } boolean isDirectory = (type.contains("
")); boolean isSymLink = (type.contains("")); boolean isJunction = (type.contains("")); // Get File Name String name = line.substring(offset); while (name.substring(0, 1).equals(" ")) { name = name.substring(1); offset++; } // Set Column Width if (isDirectory || isSymLink || isJunction) { colWidth = offset; } else { if (name.contains(" ")) { if (isNumeric(name.substring(0, name.indexOf(" ")))) { colWidth = offset + name.indexOf(" ") + 1; } else { colWidth = offset; } } else { colWidth = offset; } } } if (colWidth > 0) { // String date = line.substring(0,20); String name = line.substring(colWidth); String type = line.substring(20, colWidth); boolean isDirectory = (type.contains("")); boolean isSymLink = (type.contains("")); boolean isJunction = (type.contains("")); if (isDirectory) { if (!name.equals(".") && !name.equals("..")) files.add(name + Directory.PathSeparator); } else if (isSymLink || isJunction) { java.io.File file = null; // <--Note // that we're // serializing // to a file // which is // going to // slow things // down... if (name.contains("[") && name.contains("]")) { String link = name.substring(name.indexOf("[") + 1, name.indexOf("]")); name = name.substring(0, name.indexOf("[")).trim(); file = new java.io.File(link); } else { file = new java.io.File(path, name); } if (file.isDirectory()) name += Directory.PathSeparator; files.add(name); } else { files.add(name); } } } } } else { break; } } } // Catch Exceptions thrown by cmd.run() catch (java.io.IOException e) { } catch (InterruptedException e) { Thread.currentThread().interrupt(); } // Convert the list to an array return files.toArray(new String[files.size()]); } // ************************************************************************** // ** isNumeric // ************************************************************************** /** * Used to determine whether a string is a number. This is used in the dir() * method when parsing output from a Windows "dir" command. This method is * called to see if a string represents a file size (e.g. 12,345). Since the * number is meant to represent a file size, an attempt it made to convert * the string to a long instead of an int or double. */ private boolean isNumeric(String str) { try { if (str.contains(",")) str = str.replace(",", ""); Long.parseLong(str); return true; } catch (Exception e) { return false; } } @Override // ************************************************************************** // ** toString // ************************************************************************** /** * Returns the full path to this directory, including the directory name. */ public String toString() { return getPath(); } @Override public int hashCode() { return getFile().hashCode(); } // @Override @Override public int compareTo(Object obj) { if (obj == null) return -1; else return -obj.toString().compareTo(getPath()); } @Override // ************************************************************************** // ** equals // ************************************************************************** public boolean equals(Object obj) { if (obj instanceof Directory) { return getFile().equals(((Directory) obj).toFile()); } else if (obj instanceof java.io.File) { if (((java.io.File) obj).isDirectory()) return getFile().equals(obj); else return false; } else { return false; } } // ************************************************************************** // ** clone // ************************************************************************** /** Creates a copy of this object. */ @Override public Directory clone() { return new Directory(this.toString()); } // ************************************************************************** // ** getEvents // ************************************************************************** /** * Used to start monitoring changes made to the directory. Changes include * creating, modifying or deleting files/folders found in this directory. * Returns a list of Directory.Event(s). Clients can wait for new events * using the wait() method. Recommend removing events from the list whenever * !events.isEmpty(). * * Example: * *
	 * java.util.List events = directory.getEvents();
	 * while (true) {
	 * 
	 *     Object obj;
	 *     synchronized (events) {
	 *         while (events.isEmpty()) {
	 *             try {
	 *                 events.wait();
	 *             } catch (InterruptedException e) {
	 *             }
	 *         }
	 * 
	 *         obj = events.remove(0);
	 *     }
	 *     if (obj != null) {
	 * 
	 *         javaxt.io.Directory.Event event = (javaxt.io.Directory.Event) obj;
	 *         System.out.println(event.toString());
	 * 
	 *         // Compare files before/after the event
	 *         if (event.getEventID() == event.RENAME) {
	 *             System.out.println(event.getOriginalFile().getName() + " vs " + event.getFile().getName());
	 *         }
	 *     }
	 * 
	 * }
	 * 
*/ public List getEvents() throws Exception { if (FileSystemWatcher == null) { FileSystemWatcher = new FileSystemWatcher(this); new Thread(FileSystemWatcher).start(); } return FileSystemWatcher.getEvents(); } // ************************************************************************** // ** Stop // ************************************************************************** /** * Used to stop any worker threads that may be running (recursive search or * event monitor). */ public void stop() { if (FileSystemWatcher != null) FileSystemWatcher.stop(); try { // for (int i=0; i<20; i++) DirectorySearch.stop(); } catch (Exception e) { } } // ************************************************************************** // ** Finalize // ************************************************************************** /** * Method called by Java garbage collector to dispose operating system * resource. */ @Override protected void finalize() throws Throwable { stop(); super.finalize(); } // ************************************************************************** // ** Event Class // ************************************************************************** /** * Used to encapsulate a single event on the file system. */ public static class Event { private String file; private String orgFile; private java.util.Date date; private String action; public static final int DELETE = 0; public static final int CREATE = 1; public static final int RENAME = 2; public static final int MODIFY = 3; // ************************************************************************ // ** Constructor // ************************************************************************ /** * Creates a new instance of FileSystemEvent by parsing a string * returned from FileSystemWatcherNative.ReadDirectoryChangesW() */ protected Event(String event) { if (event != null) { event = event.trim(); if (event.length() == 0) event = null; } if (event != null) { try { // Parse event string String date = event.substring(1, event.indexOf("]")).trim(); String text = event.substring(event.indexOf("]") + 1).trim(); String path = text.substring(text.indexOf(" ")).trim(); String action = text.substring(0, text.indexOf(" ")).trim(); // Set local variables this.date = parseDate(date); this.file = path; this.action = action; } catch (Exception ex) { // ex.printStackTrace(); } } } // ************************************************************************ // ** Constructor // ************************************************************************ /** * Creates a new instance of FileSystemEvent for EventMonitor */ protected Event(String action, String file) { this.date = new java.util.Date(); this.action = action; this.file = file; } // ************************************************************************ // ** parseDate // ************************************************************************ private java.util.Date parseDate(String date) { try { return new java.text.SimpleDateFormat("EEE MMM dd HH:mm:ss yyyy").parse(date); } catch (Exception e) { return null; } } // ************************************************************************ // ** getAction // ************************************************************************ /** * Returns a decription of the event (created, modified, deleted, etc.) */ public String getAction() { return action; } protected void setAction(String action) { this.action = action; } // ************************************************************************ // ** getFile // ************************************************************************ /** * Returns the file or directory that was created, modified, or deleted. */ public String getFile() { return file; } // ************************************************************************ // ** getOriginalFile // ************************************************************************ public String getOriginalFile() { return orgFile; } protected void setOrgFile(String orgFile) { this.orgFile = orgFile; } // ************************************************************************ // ** getEventID // ************************************************************************ public final int getEventID() { if (action.equalsIgnoreCase("create")) return Event.CREATE; if (action.equalsIgnoreCase("delete")) return Event.DELETE; if (action.equalsIgnoreCase("modify")) return Event.MODIFY; if (action.equalsIgnoreCase("rename")) return Event.RENAME; return -1; } // ************************************************************************ // ** getDate // ************************************************************************ /** Returns the date/time stamp when the event occured. */ public java.util.Date getDate() { return date; } // ************************************************************************ // ** toString // ************************************************************************ /** Returns a string representation of this event. */ @Override public String toString() { if (action.equalsIgnoreCase("rename")) return "[" + date.toString() + "] " + action + " " + orgFile + " To " + file; else return "[" + date.toString() + "] " + action + " " + file; } @Override public boolean equals(Object obj) { // return obj.toString().equalsIgnoreCase(action); if (obj instanceof Event) { Event event = (Event) obj; if (event.getFile().equals(this.file) && event.getDate().equals(this.date) && event.getAction().equals(this.action)) { return true; } } return false; } } // End Event Class // ************************************************************************** // ** FileComparer Class // ************************************************************************** /** * Used to sort a list containing files/folders in alphabetical order. Note * that directories are listed first. */ private class FileComparer implements Comparator { private int z; public FileComparer(Directory dir) { if (dir == null) z = 0; else z = dir.toString().replace("\\", "/").length(); } @Override public final int compare(Object a, Object b) { String x = a.toString().toUpperCase(); String y = b.toString().toUpperCase(); x = x.replace("\\", "/").substring(z); y = y.replace("\\", "/").substring(z); // Check whether a or b is a file in the root directory. if (!x.contains("/") || !y.contains("/")) { // If both a and b are files, compare file names. Use a // multiplier // to ensure that the file falls toward the end of the list. if (!x.contains("/") && !y.contains("/")) { return (x.compareTo(y)) * 10000; } else { return 100000; } } else { // Niether a or b are in the root directory // Check whether a and b are in the same directory String dir1 = x.substring(0, x.lastIndexOf("/")); String dir2 = y.substring(0, y.lastIndexOf("/")); if (!dir1.equals(dir2)) { return dir1.compareTo(dir2); } else { // a and b are in the same directory return x.compareTo(y); } } } } // End FileComparer Class } // End Directory Class // ****************************************************************************** // ** FileFilter // ****************************************************************************** /** * Used to filter files and file names using regular expressions or java.io * FileFilters. Note that directories are always returned. * ******************************************************************************/ class FileFilter { private java.io.FileFilter fileFilter = null; private List regex = null; public FileFilter(Object filter) { if (filter == null) { filter = "*"; } if (filter instanceof java.io.FileFilter) { fileFilter = (java.io.FileFilter) filter; } if (filter instanceof String) { filter = new String[] { (String) filter }; } if (filter instanceof String[]) { regex = new ArrayList(); String[] filters = (String[]) filter; for (int i = 0; i < filters.length; i++) { regex.add(java.util.regex.Pattern.compile(getRegEx(filters[i]), java.util.regex.Pattern.CASE_INSENSITIVE)); } } } public boolean accept(String file) { if (fileFilter != null) { return accept(new java.io.File(file)); } else { file = file.replace("\\", "/"); if (file.endsWith("/")) { // then we're dealing with a directory return true; } else { file = file.substring(file.lastIndexOf("/") + 1); } for (int i = 0; i < regex.size(); i++) { java.util.regex.Matcher matcher = regex.get(i).matcher(file); if (matcher.find()) { return true; } } } return false; } public boolean accept(java.io.File file) { if (file.isDirectory()) { return true; } else { if (fileFilter != null) { return (fileFilter.accept(file)); } else { return accept(file.toString()); } } } // ************************************************************************** // ** usesIOFilter // ************************************************************************** /** * Used to indicate whether this filter relies on a java.io.FileFilter */ public boolean usesIOFilter() { return (fileFilter != null); } // ************************************************************************** // ** getRegEx // ************************************************************************** /** * Used to convert a wildcard (e.g. "*.txt") into a regular expression. */ private String getRegEx(String wildcardSearch) { String regex = wildcardSearch.trim(); // if (!regex.startsWith("*")) regex = "^" + regex; if (!regex.endsWith("*")) regex += "$"; if (regex.endsWith("*")) { // regex = regex.substring(0, regex.length()-1) + "/"; regex = regex.substring(0, regex.length() - 1) + ")"; if (regex.contains("*")) { regex = regex.substring(0, regex.lastIndexOf("*") + 1) + "(" + regex.substring(regex.lastIndexOf("*") + 1); } else { regex = "(" + regex; } } regex = regex.replace(".", "\\."); regex = regex.replace("*", ".*"); // System.out.println(regex); return regex; } }// End FileFilter Class // ****************************************************************************** // ** Directory Search // ****************************************************************************** /** * Thread used crawl through a file system and find files/folders. This is a * recursive search and may take some time to complete. * ******************************************************************************/ class DirectorySearch implements Runnable { private static java.util.concurrent.ConcurrentHashMap map; private static java.util.concurrent.ConcurrentHashMap lut; // ************************************************************************** // ** Constructor // ************************************************************************** /** * Creates a new instance of DirectorySearch. Note that this thread needs to * be named! Unfortunately, this has to happen outside of this class. * Example: * *
	 * Thread t = new Thread(new DirectorySearch(filter, files));
	 * t.setName("DirectorySearch-" + id);
	 * t.start();
	 * 
* * @param filter * A file filter. * * @param items * A List (e.g. LinkedList) used to store items found in this * directory. */ public DirectorySearch(FileFilter filter, List items, long directoryID, int numThreads) { String parentThread = Thread.currentThread().getName(); // System.out.println(parentThread); if (map == null) { map = new ConcurrentHashMap(); } if (getVars() == null) { createVars(filter, items, directoryID, numThreads); } if (lut == null) lut = new ConcurrentHashMap(); synchronized (lut) { if (lut.get(parentThread) == null) lut.put(parentThread, directoryID); else { // System.out.println(parentThread + " entering wait state..."); while (!lut.isEmpty()) { try { lut.wait(); } catch (InterruptedException e) { // If interrupted, return immediately Thread.currentThread().interrupt(); return; } } // System.out.println(parentThread + " exited wait state!"); createVars(filter, items, directoryID, numThreads); lut.put(parentThread, directoryID); lut.notifyAll(); } } // System.out.println(parentThread + " " + lut.get(parentThread) + " vs // " + directoryID); } // ************************************************************************** // ** createVars // ************************************************************************** /** * Used to instantiate local variables used by this thread and it's * siblings. The variables are all stored in a hashmap and accessed via a * directoryID. */ private static void createVars(FileFilter filter, List items, long directoryID, int numThreads) { synchronized (map) { ConcurrentHashMap vars = new ConcurrentHashMap(); vars.put("items", items); vars.put("filter", filter); vars.put("pool", new java.util.LinkedList()); vars.put("path", new java.util.LinkedList()); vars.put("status", new java.util.LinkedList()); vars.put("threads", new LinkedList()); // <--list of active threads vars.put("activatedThreads", new ConcurrentHashMap()); vars.put("numThreads", numThreads); map.put(directoryID, vars); } } // ************************************************************************** // ** getVars // ************************************************************************** /** * Used to retrieve variables associated with this thread. */ private static ConcurrentHashMap getVars() { String currentThread = Thread.currentThread().getName(); if (currentThread.startsWith("DirectorySearch_")) { Long directoryID = getDirectoryID(); return (ConcurrentHashMap) map.get(directoryID); } else { if (lut == null) return null; Long directoryID = null; synchronized (lut) { directoryID = (Long) lut.get(currentThread); } // System.out.println(currentThread + " vs " + directoryID); if (directoryID == null) return null; else return (ConcurrentHashMap) map.get(directoryID); } } // ************************************************************************** // ** deleteCache // ************************************************************************** public static void deleteCache() { if (map != null) { ConcurrentHashMap vars = getVars(); if (vars != null) { synchronized (vars) { List path = (List) vars.get("path"); List items = (List) vars.get("items"); List status = (List) vars.get("status"); synchronized (path) { path.clear(); } synchronized (items) { items.clear(); } synchronized (status) { status.clear(); } vars.remove("startTime"); vars.remove("root"); } } } } private static Long getDirectoryID() { try { String id = Thread.currentThread().getName(); id = id.substring(id.indexOf("_") + 1); id = id.substring(0, id.indexOf("-")); // System.out.println(id + " vs " + // Thread.currentThread().getName()); return Long.valueOf(id).longValue(); } catch (Exception e) { return 0L; } } // ************************************************************************** // ** Stop // ************************************************************************** /** Used to stop the current thread. */ public static void stop() { updatePool(null); } // ************************************************************************** // ** updatePool // ************************************************************************** /** Used to add a new directory to the pool. */ public static void updatePool(Directory directory) { ConcurrentHashMap vars = getVars(); Long startTime = null; if (vars.get("startTime") != null) startTime = (Long) vars.get("startTime"); // if start time is null, then this is the first time the pool has been // modified if (startTime == null) { synchronized (vars) { vars.put("startTime", getStartTime()); vars.put("root", directory); vars.notifyAll(); } } boolean updatePool = true; if (directory == null) { if (Thread.currentThread().getName().equalsIgnoreCase("Finalizer")) { updatePool = false; } } if (updatePool) { List pool = (List) vars.get("pool"); synchronized (pool) { pool.add(directory); pool.notifyAll(); } } } // ************************************************************************** // ** Add File // ************************************************************************** /** Used to add a file to the list of items found. */ private static void addFile(File file) { ConcurrentHashMap vars = getVars(); List items = (List) vars.get("items"); synchronized (items) { items.add(file); items.notifyAll(); } } // ************************************************************************** // ** Add Directory // ************************************************************************** /** Used to add a directory to the list of items found. */ private static void addDirectory(Directory directory) { ConcurrentHashMap vars = getVars(); List items = (List) vars.get("items"); Directory root = (Directory) vars.get("root"); synchronized (items) { if (!directory.equals(root)) { items.add(directory); items.notifyAll(); } } } private static void addPath(Directory directory) { List path = (List) getVars().get("path"); synchronized (path) { path.add(directory.toString()); } } private static void removePath(Directory directory) { List path = (List) getVars().get("path"); synchronized (path) { path.remove(directory.toString()); } } // ************************************************************************** // ** Get Status // ************************************************************************** /** * Used to retrieve the status object. The status object is a List that will * remain empty until all of the threads have completed searching the file * system. While status.isEmpty() call status.wait(); */ public static List getStatus() { ConcurrentHashMap vars = getVars(); List status = (List) vars.get("status"); return status; } // ************************************************************************** // ** Update Status // ************************************************************************** /** * Used to insert an entry into the status object. This is used to notify * the client that all threads have completed searching the file system. */ private static void updateStatus() { ConcurrentHashMap vars = getVars(); List status = (List) vars.get("status"); List items = (List) vars.get("items"); Long startTime = (Long) vars.get("startTime"); synchronized (status) { status.add("ellapsedTime = " + getEllapsedTime(startTime) + " ms"); status.notifyAll(); // System.out.println(Thread.currentThread().getName() + " Updated // Status!"); } synchronized (items) { items.add(null); items.notifyAll(); } ((List) vars.get("pool")).clear(); ((List) vars.get("path")).clear(); ((List) vars.get("threads")).clear(); ((ConcurrentHashMap) vars.get("activatedThreads")).clear(); // Update the lut synchronized (lut) { long directoryID = getDirectoryID(); java.util.Iterator it = lut.keySet().iterator(); while (it.hasNext()) { String parentThread = (String) it.next(); if ((Long) lut.get(parentThread) == directoryID) { lut.remove(parentThread); lut.notifyAll(); break; } } } } // ************************************************************************** // ** Get Items // ************************************************************************** /** * Used to retrieve a list of files and folders found in this directory. */ public static List getItems() { return (List) getVars().get("items"); } // ************************************************************************** // ** Run // ************************************************************************** /** Processes entries in the pool. Waits and add children to the array. */ @Override public void run() { // Get shared variables ConcurrentHashMap vars = getVars(); if (vars == null) return; // <--This should never happen! List threads = (List) vars.get("threads"); ConcurrentHashMap activatedThreads = (ConcurrentHashMap) vars.get("activatedThreads"); List path = (List) vars.get("path"); List pool = (List) vars.get("pool"); FileFilter filter = (FileFilter) vars.get("filter"); int numThreads = (Integer) vars.get("numThreads"); String threadID = Thread.currentThread().getName(); while (true) { Directory dir; // Wait for new directories to be added to the pool synchronized (pool) { while (pool.isEmpty()) { try { pool.wait(); } catch (InterruptedException e) { // If interrupted, return immediately Thread.currentThread().interrupt(); return; } } // Update the list of active threads synchronized (threads) { threads.add(threadID); } synchronized (activatedThreads) { if (activatedThreads.get(threadID) == null) { activatedThreads.put(threadID, true); } } Object obj = pool.get(0); if (obj == null) { // System.out.println("Terminating Thread: " + threadID); synchronized (threads) { threads.remove(threadID); } synchronized (activatedThreads) { activatedThreads.replace(threadID, false); if (activatedThreads.size() == numThreads) { if (!activatedThreads.containsValue(true)) { updateStatus(); } } } return; } else { dir = (Directory) obj; pool.remove(obj); pool.notifyAll(); } } // Process directory if (dir != null) { // Notify Path/Status Object of new task addPath(dir); // Add subdirectories to the processing pool and insert files // into file array Object[] items = dir.listFiles(); if (items != null) { for (int i = 0; i < items.length; i++) { Object obj = items[i]; boolean isDirectory = false; boolean accept = false; if (obj instanceof String) { String s = (String) obj; accept = filter.accept(s); isDirectory = s.replace("\\", "/").endsWith("/"); } else if (obj instanceof java.io.File) { java.io.File f = (java.io.File) obj; accept = filter.accept(f); isDirectory = f.isDirectory(); } if (accept) { if (isDirectory) { // Add directory to the array and update the // pool Directory d = null; if (obj instanceof String) { d = (new Directory((String) obj)); } else if (obj instanceof java.io.File) { d = (new Directory((java.io.File) obj)); } if (d != null) { addDirectory(d); updatePool(d); } } else { // Add File to the array if (obj instanceof String) { addFile(new File((String) obj)); } else if (obj instanceof java.io.File) { addFile(new File((java.io.File) obj)); } } } } } // Notify Path/Status Object of task completion removePath(dir); synchronized (threads) { threads.remove(threadID); } // Check whether we're done processing all the directories and // notify the client synchronized (path) { if (path.isEmpty()) { synchronized (pool) { if (pool.isEmpty()) { synchronized (threads) { if (threads.isEmpty()) { // System.out.println("Terminating // Thread: " + threadID + "*"); synchronized (activatedThreads) { activatedThreads.replace(threadID, false); } updatePool(null); return; } } } } } } } /* * else{ //Found null entry. This is our que to stop waiting for new * directories... //System.out.println("Terminating Thread: " + * Thread.currentThread().getName()); * * * * * synchronized (threads){ String threadID = * Thread.currentThread().getName(); threads.remove(threadID); } * * * synchronized (activatedThreads){ * activatedThreads.replace(Thread.currentThread().getName(), * false); if (activatedThreads.containsValue(true)){ * updatePool(null); } else{ * * if (activatedThreads.size() (interval * 2)) { // System.out.println((startTime-lastUpdate) + " vs " + // (interval)); List orgIndex = index; List newIndex = createIndex(); for (int i = 0; i < newIndex.size(); i++) { Item item = (Item) newIndex.get(i); if (orgIndex.contains(item)) { int x = orgIndex.indexOf(item); Item orgItem = (Item) orgIndex.get(x); orgIndex.remove(x); if (item.isDirectory() == false) { if (item.getSize() != orgItem.getSize() || item.getDate() != orgItem.getDate()) { addEvent("Modify", item.getPath()); } } } else { addEvent("Create", item.getPath()); } } if (orgIndex.size() > 0) { for (int i = 0; i < orgIndex.size(); i++) { Item item = (Item) orgIndex.get(i); addEvent("Delete", item.getPath()); } orgIndex.clear(); } index = newIndex; long endTime = java.util.Calendar.getInstance().getTimeInMillis(); interval = endTime - startTime; // System.out.println(interval); lastUpdate = endTime; } // end if }// end run private void addEvent(String action, String file) { Directory.Event event = new Directory.Event(action, file); synchronized (events) { events.add(event); events.notifyAll(); } } // ************************************************************************ // ** createIndex // ************************************************************************ /** * Used to create an array of files and folders found in a directory. * Also used to update the interval variable used by the EventMonitor. */ private List createIndex() { List index = new LinkedList(); java.util.List files = directory.getChildren(true, null, false); Object obj; while (true) { synchronized (files) { while (files.isEmpty()) { try { files.wait(); } catch (InterruptedException e) { break; } } obj = files.remove(0); files.notifyAll(); } if (obj == null) { break; } else { index.add(new Item(obj)); } } return index; } // ************************************************************************ // ** Item Class // ************************************************************************ /** * Used to represent a single file or folder. Records the size and date * of the file/folder at the time this object is instantiated. This * information is used to determine which items have changed state * within the EventMonitor class. */ private class Item { private String path; private long size; private long date; private boolean isDirectory; public Item(Object obj) { java.io.File file = null; if (obj instanceof File) file = ((File) obj).toFile(); else if (obj instanceof Directory) file = ((Directory) obj).toFile(); else if (obj instanceof java.io.File) file = (java.io.File) obj; if (file != null) { this.path = file.getPath(); this.size = file.length(); this.date = file.lastModified(); this.isDirectory = file.isDirectory(); } } public String getPath() { return path; } public long getSize() { return size; } public long getDate() { return date; } public boolean isDirectory() { return isDirectory; } @Override public String toString() { return path; } @Override public boolean equals(Object obj) { if (obj instanceof Item) { Item item = (Item) obj; return item.getPath().equals(this.getPath()); } else { return false; } } } // End Item } // End EventMonitor Class // ************************************************************************** // ** Finalize // ************************************************************************** /** * Method called by Java garbage collector to dispose operating system * resource. */ @Override protected void finalize() throws Throwable { if (this.osHandle != null) { FileSystemWatcherNative.FindCloseChangeNotification(this.osHandle.longValue()); } } } // End FileSystemWatcher Class // ****************************************************************************** // ** FileSystemWatcherNative Class // ****************************************************************************** /** * Java wrapper of Windows native APIs for file system monitoring. The name of * the methods in this class corresponds to the name of Windows native APIs. You * might want to check Windows Platform SDK for detailed technical information. *

* * Also note that if you change the package or method name of this class, you * need to use javah to regenerate a C header file and recompile the dll file. * As far as I know, there is no way around it because of the way JVM binds to * native dll. * ******************************************************************************/ final class FileSystemWatcherNative { static { } public static native long FindFirstChangeNotification(String lpPathName, boolean bWatchSubtree, int dwNotifyFilter) throws Exception; public static native void FindNextChangeNotification(long hChangeHandle) throws Exception; public static native void FindCloseChangeNotification(long hChangeHandle) throws Exception; public static native int WaitForSingleObject(long hHandle, int dwTimeoutMilliseconds); public static native String ReadDirectoryChangesW(); // ************************************************************************** // ** Static Local Variables // ************************************************************************** /** A constant value representing infinite. */ public static final int INFINITE = 0xFFFFFFFF; // (WinBase.h) /** A wait return value indicating a failed wait. */ public static final int WAIT_FAILED = 0xFFFFFFFF; /** * A wait return value indicating that the specified object is a mutex * object that was not released by the thread. */ public static final int WAIT_ABANDONED = 0x00000080; /** * A wait return value indicating The state of the specified object is * signaled. */ public static final int WAIT_OBJECT_0 = 0x00000000; /** The time-out interval elapsed, and the object's state is nonsignaled. */ public static final int WAIT_TIMEOUT = 0x00000102; } // End FileSystemWatcherNative





© 2015 - 2025 Weber Informatics LLC | Privacy Policy