Commits

mquigley  committed f6386fe Merge

Merge current release into release branch.

  • Participants
  • Parent commits eded6a0, cd5962f

Comments (0)

Files changed (41)

File .classpath

-<?xml version="1.0" encoding="UTF-8"?>
-<classpath>
-	<classpathentry kind="src" path="src/java"/>
-	<classpathentry kind="src" path="src/test"/>
-	<classpathentry kind="lib" path="lib/apache-log4j-1.2.15/log4j-1.2.15.jar"/>
-	<classpathentry kind="lib" path="lib/junit-4.4/junit-4.4.jar"/>
-	<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
-	<classpathentry kind="lib" path="lib/slf4j-1.6.1/slf4j-api-1.6.1.jar"/>
-	<classpathentry kind="lib" path="lib/slf4j-1.6.1/slf4j-log4j12-1.6.1.jar"/>
-	<classpathentry kind="output" path=".eclipse-build"/>
-</classpath>
-.eclipse-build
-build
+.classpath
+.project
+target

File .project

-<?xml version="1.0" encoding="UTF-8"?>
-<projectDescription>
-	<name>Filesystem</name>
-	<comment></comment>
-	<projects>
-	</projects>
-	<buildSpec>
-		<buildCommand>
-			<name>org.eclipse.jdt.core.javabuilder</name>
-			<arguments>
-			</arguments>
-		</buildCommand>
-	</buildSpec>
-	<natures>
-		<nature>org.eclipse.jdt.core.javanature</nature>
-	</natures>
-</projectDescription>
+		   GNU LESSER GENERAL PUBLIC LICENSE
+                       Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+  This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+  0. Additional Definitions.
+
+  As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+  "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+  An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+  A "Combined Work" is a work produced by combining or linking an
+Application with the Library.  The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+  The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+  The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+  1. Exception to Section 3 of the GNU GPL.
+
+  You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+  2. Conveying Modified Versions.
+
+  If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+   a) under this License, provided that you make a good faith effort to
+   ensure that, in the event an Application does not supply the
+   function or data, the facility still operates, and performs
+   whatever part of its purpose remains meaningful, or
+
+   b) under the GNU GPL, with none of the additional permissions of
+   this License applicable to that copy.
+
+  3. Object Code Incorporating Material from Library Header Files.
+
+  The object code form of an Application may incorporate material from
+a header file that is part of the Library.  You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+   a) Give prominent notice with each copy of the object code that the
+   Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the object code with a copy of the GNU GPL and this license
+   document.
+
+  4. Combined Works.
+
+  You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+   a) Give prominent notice with each copy of the Combined Work that
+   the Library is used in it and that the Library and its use are
+   covered by this License.
+
+   b) Accompany the Combined Work with a copy of the GNU GPL and this license
+   document.
+
+   c) For a Combined Work that displays copyright notices during
+   execution, include the copyright notice for the Library among
+   these notices, as well as a reference directing the user to the
+   copies of the GNU GPL and this license document.
+
+   d) Do one of the following:
+
+       0) Convey the Minimal Corresponding Source under the terms of this
+       License, and the Corresponding Application Code in a form
+       suitable for, and under terms that permit, the user to
+       recombine or relink the Application with a modified version of
+       the Linked Version to produce a modified Combined Work, in the
+       manner specified by section 6 of the GNU GPL for conveying
+       Corresponding Source.
+
+       1) Use a suitable shared library mechanism for linking with the
+       Library.  A suitable mechanism is one that (a) uses at run time
+       a copy of the Library already present on the user's computer
+       system, and (b) will operate properly with a modified version
+       of the Library that is interface-compatible with the Linked
+       Version.
+
+   e) Provide Installation Information, but only if you would otherwise
+   be required to provide such information under section 6 of the
+   GNU GPL, and only to the extent that such information is
+   necessary to install and execute a modified version of the
+   Combined Work produced by recombining or relinking the
+   Application with a modified version of the Linked Version. (If
+   you use option 4d0, the Installation Information must accompany
+   the Minimal Corresponding Source and Corresponding Application
+   Code. If you use option 4d1, you must provide the Installation
+   Information in the manner specified by section 6 of the GNU GPL
+   for conveying Corresponding Source.)
+
+  5. Combined Libraries.
+
+  You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+   a) Accompany the combined library with a copy of the same work based
+   on the Library, uncombined with any other library facilities,
+   conveyed under the terms of this License.
+
+   b) Give prominent notice with the combined library that part of it
+   is a work based on the Library, and explaining where to find the
+   accompanying uncombined form of the same work.
+
+  6. Revised Versions of the GNU Lesser General Public License.
+
+  The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+  Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+  If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.

File build.xml

-<project name="Filesystem" default="world">
-	<property name="version" value="1.0.0"/>
-	
-	<target name="world" depends="package"/>
-
-	<target name="compile">
-		<mkdir dir="build/classes/"/>
-
-		<javac srcdir="src/java/" destdir="build/classes/" verbose="off" debug="on" includeantruntime="off" />
-	</target>
-
-	<target name="package" depends="compile">
-		<mkdir dir="build/dist/"/>
-
-		<jar jarfile="build/dist/quigley-filesystem-${version}.jar">
-			<fileset dir="build/classes/"/>
-		</jar>
-	</target>
-
-	<target name="clean">
-		<delete dir="build/"/>
-	</target>
-</project>

File lib/apache-log4j-1.2.15/log4j-1.2.15.jar

Binary file removed.

File lib/junit-4.4/junit-4.4.jar

Binary file removed.

File lib/slf4j-1.6.1/slf4j-api-1.6.1.jar

Binary file removed.

File lib/slf4j-1.6.1/slf4j-log4j12-1.6.1.jar

Binary file removed.
+<project>
+	<modelVersion>4.0.0</modelVersion>
+
+	<parent>
+		<groupId>org.sonatype.oss</groupId>
+		<artifactId>oss-parent</artifactId>
+		<version>7</version>
+	</parent>
+
+	<groupId>com.quigley</groupId>
+	<artifactId>filesystem</artifactId>
+
+	<packaging>jar</packaging>
+
+	<name>Filesystem Framework</name>
+	<version>0.0.2</version>
+	<url>http://www.quigley.com/</url>
+	<description>
+		The Filesystem Framework provides smart file system path manipulation functions, and provides operations for working on file system trees.
+	</description>
+	<licenses>
+		<license>
+			<name>GNU Lesser General Public License</name>
+			<url>http://www.gnu.org/licenses/lgpl.txt</url>
+			<distribution>repo</distribution>
+		</license>
+	</licenses>
+	<scm>
+		<connection>scm:hg:https://bitbucket.org/mquigley/filesystem</connection>
+		<developerConnection>scm:hg:https://bitbucket.org/mquigley/filesystem</developerConnection>
+		<url>https://bitbucket.org/mquigley/filesystem</url>
+	</scm>
+	<developers>
+		<developer>
+			<id>mquigley</id>
+			<name>Michael Quigley</name>
+			<email>michael@quigley.com</email>
+		</developer>
+	</developers>
+
+	<properties>
+		<slf4j.version>1.6.6</slf4j.version>
+	</properties>
+
+	<dependencies>
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-api</artifactId>
+			<version>${slf4j.version}</version>
+		</dependency>
+		<dependency>
+			<groupId>org.slf4j</groupId>
+			<artifactId>slf4j-log4j12</artifactId>
+			<version>${slf4j.version}</version>
+			<scope>test</scope>
+		</dependency>
+		<dependency>
+			<groupId>log4j</groupId>
+			<artifactId>log4j</artifactId>
+			<version>1.2.17</version>
+			<scope>test</scope>
+		</dependency>		
+
+		<dependency>
+			<groupId>junit</groupId>
+			<artifactId>junit</artifactId>
+			<version>4.10</version>
+			<scope>test</scope>
+		</dependency>
+	</dependencies>
+</project>

File src/java/com/quigley/filesystem/FilesystemException.java

-package com.quigley.filesystem;
-
-@SuppressWarnings("serial")
-public class FilesystemException extends RuntimeException {
-	public FilesystemException() {
-		super();
-	}
-	public FilesystemException(String message, Throwable cause) {
-		super(message, cause);
-	}
-	public FilesystemException(String message) {
-		super(message);
-	}
-	public FilesystemException(Throwable cause) {
-		super(cause);
-	}
-}

File src/java/com/quigley/filesystem/FilesystemInventoryVisitor.java

-package com.quigley.filesystem;
-
-import java.util.ArrayList;
-import java.util.List;
-
-public class FilesystemInventoryVisitor implements FilesystemVisitor {
-	public FilesystemInventoryVisitor() {
-		paths = new ArrayList<FilesystemPath>();
-		includeDirectories = true;
-		excludeTokens = new ArrayList<String>();
-		includeTokens = new ArrayList<String>();
-	}
-	
-	public void visit(FilesystemPath path) {
-		boolean include = true;
-		if(includeTokens.size() > 0) {
-			include = false;
-			for(String includeToken : includeTokens) {
-				if(path.asString().indexOf(includeToken) != -1) {
-					include = true;
-					break;
-				}
-			}
-		}
-		boolean exclude = false;
-		if(excludeTokens.size() > 0) {
-			for(String excludeToken : excludeTokens) {
-				if(path.asString().indexOf(excludeToken) != -1) {
-					exclude = true;
-					break;
-				}
-			}
-		}
-		if(include && !exclude) {
-			if(includeDirectories || !path.asFile().isDirectory()) {
-				paths.add(path);
-			}
-		}
-	}
-	
-	public boolean isIncludeDirectories() {
-		return includeDirectories;
-	}
-	public void setIncludeDirectories(boolean includeDirectories) {
-		this.includeDirectories = includeDirectories;
-	}
-
-	public List<String> getExcludeTokens() {
-		return excludeTokens;
-	}
-	public void setExcludeTokens(List<String> excludeTokens) {
-		if(excludeTokens != null) {
-			this.excludeTokens = excludeTokens;
-		}
-	}
-	public void addExcludeToken(String excludeToken) {
-		excludeTokens.add(excludeToken);
-	}
-
-	public List<String> getIncludeTokens() {
-		return includeTokens;
-	}
-	public void setIncludeTokens(List<String> includeTokens) {
-		if(includeTokens != null) {
-			this.includeTokens = includeTokens;
-		}
-	}
-	public void addIncludeToken(String includeToken) {
-		includeTokens.add(includeToken);
-	}
-
-	public List<FilesystemPath> getPaths() {
-		return paths;
-	}
-
-	protected boolean includeDirectories;
-	protected List<String> excludeTokens;
-	protected List<String> includeTokens;
-	
-	protected List<FilesystemPath> paths;
-}

File src/java/com/quigley/filesystem/FilesystemIterator.java

-package com.quigley.filesystem;
-
-import java.io.File;
-
-public class FilesystemIterator {
-    private FilesystemPath root;
-    private FilesystemVisitor visitor;
-
-    public FilesystemIterator(FilesystemPath root, FilesystemVisitor visitor) {
-        this.root = root;
-        this.visitor = visitor;
-    }
-
-    public void iterate() throws FilesystemException {
-        File rootF = root.asFile();
-        if(rootF.exists() && rootF.canRead() && rootF.isDirectory()) {
-            visitor.visit(root);
-
-            File[] contents = rootF.listFiles();
-            for(File f : contents) {
-                FilesystemPath fPath = new FilesystemPath(root).add(f.getName());
-
-                if(f.isDirectory()) {
-                    FilesystemIterator subIter = new FilesystemIterator(fPath, visitor);
-                    subIter.iterate();
-
-                } else {
-                    visitor.visit(fPath);
-                }
-            }
-
-        } else {
-            throw new FilesystemException(root.asString() + " is not a valid directory!");
-        }
-    }
-}

File src/java/com/quigley/filesystem/FilesystemOperation.java

-package com.quigley.filesystem;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.FileInputStream;
-import java.io.FileOutputStream;
-import java.io.FileReader;
-import java.io.FileWriter;
-import java.io.IOException;
-import java.nio.channels.FileChannel;
-import java.util.ArrayList;
-import java.util.List;
-
-public class FilesystemOperation {
-	public static void copy(FilesystemPath source, FilesystemPath dest) throws FilesystemException {
-		copy(source.asFile(), dest.asFile());
-	}
-
-	public static void copy(File source, File dest) throws FilesystemException {
-		try {
-			FileChannel sourceChannel = new FileInputStream(source).getChannel();
-			FileChannel destChannel = new FileOutputStream(dest).getChannel();
-
-			sourceChannel.transferTo(0, sourceChannel.size(), destChannel);
-
-			sourceChannel.close();
-			destChannel.close();
-
-		} catch (Exception e) {
-			throw new FilesystemException("Unable to copy", e);
-		}
-	}
-	
-	public static List<FilesystemPath> copyTree(FilesystemPath sourceTree, FilesystemPath destTree, List<String> includeTokens, List<String> excludeTokens) throws FilesystemException {
-		FilesystemInventoryVisitor visitor = new FilesystemInventoryVisitor();
-		visitor.setExcludeTokens(excludeTokens);
-		visitor.setIncludeTokens(includeTokens);
-		visitor.setIncludeDirectories(false);
-		FilesystemIterator iterator = new FilesystemIterator(sourceTree, visitor);
-		iterator.iterate();
-		
-		List<FilesystemPath> copiedFiles = new ArrayList<FilesystemPath>();
-		for(FilesystemPath sourcePath : visitor.getPaths()) {
-			FilesystemPath relativeSourcePath = FilesystemPath.removeCommonParent(sourcePath, sourceTree);
-			copiedFiles.add(relativeSourcePath);
-			FilesystemPath destPath = destTree.add(relativeSourcePath);
-			destPath.parent().asFile().mkdirs();
-			copy(sourcePath, destPath);
-		}
-		
-		return copiedFiles;
-	}
-
-	public static String readFileAsString(File path) throws IOException {
-		StringBuilder fileData = new StringBuilder(1000);
-		BufferedReader reader = new BufferedReader(new FileReader(path));
-
-		char[] buf = new char[1024];
-		int numRead = 0;
-		while ((numRead = reader.read(buf)) != -1) {
-			fileData.append(buf, 0, numRead);
-		}
-		reader.close();
-
-		return fileData.toString();
-	}
-	
-	public static void writeStringToFile(String data, File path) throws IOException {
-		FileWriter writer = new FileWriter(path);
-		writer.write(data);
-		writer.close();
-	}
-	
-	public static boolean recursiveDelete(File path) {
-		if(path.exists()) {
-			if(path.isDirectory()) {
-				for(String child : path.list()) {
-					if(!recursiveDelete(new File(path, child))) {
-						return false;
-					}
-				}
-			}
-			return path.delete();
-		}
-		return false;
-	}
-}

File src/java/com/quigley/filesystem/FilesystemPath.java

-package com.quigley.filesystem;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.LinkedList;
-import java.util.List;
-
-public class FilesystemPath {
-    private boolean isAbsolute;
-    private List<String> elements;
-
-    public FilesystemPath(String pathString) {
-        pathString = FilesystemPath.normalize(pathString);
-        if(pathString.length() == 0) {
-        	isAbsolute = true;
-        	elements = new ArrayList<String>();
-        } else
-        if(pathString.substring(0, 1).equals("/")) {
-            isAbsolute = true;
-            pathString = pathString.substring(1, pathString.length());
-        }
-        if(pathString.length() > 0) {
-	        String[] componentArray = pathString.split("/");
-	        elements = new ArrayList<String>(componentArray.length);
-	        for(String component : componentArray) {
-	            elements.add(component);
-	        }
-        }
-    }
-
-    public FilesystemPath(FilesystemPath source) {
-        elements = new LinkedList<String>();
-        for(int i = 0; i < source.size(); i++) {
-            elements.add(source.get(i));
-        }
-        isAbsolute = source.isAbsolute();
-    }
-    
-    public FilesystemPath(List<String> elements) {
-    	this.elements = elements;
-    }
-
-    public FilesystemPath toAbsolute() {
-    	FilesystemPath absolutePath = new FilesystemPath(this.asFile().getAbsolutePath());
-    	if(absolutePath.getLast().equals(".")) {
-    		absolutePath = absolutePath.removeLast();
-    	}
-    	return absolutePath;
-    }
-    
-    public boolean isAbsolute() {
-        return isAbsolute;
-    }
-    public void setAbsolute(boolean absolute) {
-        isAbsolute = absolute;
-    }
-
-    public FilesystemPath add(String element) {
-    	List<String> elementsCopy = new ArrayList<String>(elements);
-    	
-        elementsCopy.add(element);
-        
-        FilesystemPath pathCopy = new FilesystemPath(elementsCopy);
-        pathCopy.setAbsolute(isAbsolute);
-        
-        return pathCopy;
-    }
-    
-    public FilesystemPath add(int element) {
-    	return add("" + element);
-    }
-    
-    public FilesystemPath add(long element) {
-    	return add("" + element);
-    }
-
-    public FilesystemPath add(FilesystemPath p) {
-    	List<String> elementsCopy = new ArrayList<String>(elements);
-    	
-        for(int i = 0; i < p.size(); i++) {
-            elementsCopy.add(p.get(i));
-        }
-
-        FilesystemPath pathCopy = new FilesystemPath(elementsCopy);
-        pathCopy.setAbsolute(isAbsolute);
-        
-        return pathCopy;
-    }
-
-    public FilesystemPath set(int idx, String component) {
-    	List<String> elementsCopy = new ArrayList<String>(elements);
-    	
-        elementsCopy.set(idx, component);
-
-        FilesystemPath pathCopy = new FilesystemPath(elementsCopy);
-        pathCopy.setAbsolute(isAbsolute);
-        
-        return pathCopy;
-    }    
-    
-    public FilesystemPath remove(int idx) {
-    	List<String> elementsCopy = new ArrayList<String>(elements);
-    	
-        elementsCopy.remove(idx);
-        
-        FilesystemPath pathCopy = new FilesystemPath(elementsCopy);
-        pathCopy.setAbsolute(isAbsolute);
-        
-        return pathCopy;
-    }
-    
-    public FilesystemPath removeFirst() {
-    	List<String> elementsCopy = new ArrayList<String>(elements);
-    	
-    	elementsCopy.remove(0);
-    	
-    	FilesystemPath pathCopy = new FilesystemPath(elementsCopy);
-    	
-    	return pathCopy;
-    }
-    
-    public FilesystemPath removeFirst(int count) {
-    	List<String> elementsCopy = new ArrayList<String>(elements);
-    	
-    	elementsCopy = elementsCopy.subList(count, elementsCopy.size());
-    	
-    	FilesystemPath pathCopy = new FilesystemPath(elementsCopy);
-    	
-    	return pathCopy;
-    }
-    
-    public FilesystemPath removeLast() {
-    	List<String> elementsCopy = new ArrayList<String>(elements);
-    	
-    	elementsCopy.remove(elements.size() - 1);
-    	
-    	FilesystemPath pathCopy = new FilesystemPath(elementsCopy);
-    	pathCopy.setAbsolute(isAbsolute);
-    	
-    	return pathCopy;
-    }
-    
-    public FilesystemPath removeExtension() {
-    	return new FilesystemPath(removeExtension(this.asString()));
-    }
-    
-    public FilesystemPath parent() {
-    	return removeLast();
-    }
-
-    public String get(int index) {
-        return elements.get(index);
-    }
-    
-    public String getLast() {
-    	if(size() > 0) {
-    		return elements.get(elements.size() - 1);
-    		
-    	} else {
-    		return null;
-    	}
-    }
-
-    public String getExtension() {
-        if(elements.size() < 1) {
-            return null;
-        }
-        String element = elements.get(elements.size() - 1);
-        int extStart = element.lastIndexOf(".");
-        if(extStart != -1) {
-            return element.substring(extStart + 1);
-        } else {
-            return null;
-        }
-    }    
-    
-    public boolean contains(String match) {
-    	for(String element : elements) {
-    		if(element.equals(match)) {
-    			return true;
-    		}
-    	}
-    	return false;
-    }
-    
-    public String navigate(FilesystemPath toPath) {
-    	if(toPath.asString().equals(this.asString())) {
-    		return this.getLast();
-    	}
-    	
-		StringBuilder p = new StringBuilder();
-
-		FilesystemPath shortenedCurrentPath = FilesystemPath.removeCommonParent(this, toPath);
-		for(int i = 0; i < shortenedCurrentPath.size() - 1; i++) {
-			p.append("../");
-		}
-		
-		FilesystemPath shortenedTargetPath = FilesystemPath.removeCommonParent(toPath, this);
-		p.append(shortenedTargetPath.asString());
-		
-		return p.toString();    	
-    }
-    
-    public String asString() {
-    	return toString();
-    }
-
-    public int size() {
-        return elements.size();
-    }    
-    
-    public File asFile() {
-        return new File(this.asString());
-    }
-
-    @Override
-	public int hashCode() {
-		final int prime = 31;
-		int result = 1;
-		result = prime * result
-				+ ((elements == null) ? 0 : elements.hashCode());
-		result = prime * result + (isAbsolute ? 1231 : 1237);
-		return result;
-	}
-
-	@Override
-	public boolean equals(Object obj) {
-		if (this == obj)
-			return true;
-		if (obj == null)
-			return false;
-		if (getClass() != obj.getClass())
-			return false;
-		final FilesystemPath other = (FilesystemPath) obj;
-		if (elements == null) {
-			if (other.elements != null)
-				return false;
-		} else if (!elements.equals(other.elements))
-			return false;
-		if (isAbsolute != other.isAbsolute)
-			return false;
-		return true;
-	}
-
-	public String toString() {
-        StringBuilder sb = new StringBuilder();
-        for(int i = 0; i < elements.size(); i++) {
-            if(isAbsolute || i > 0) {
-                sb.append("/");
-            }
-            sb.append(elements.get(i));
-        }
-        return sb.toString();
-    }
-
-    /*
-     * Static filesystem path operations.
-     */
-    
-    public static String normalize(String pathString) {
-        while(pathString.indexOf("\\") != -1) {
-            pathString = pathString.replace('\\', '/');
-        }
-        while(pathString.indexOf("//") != -1) {
-            pathString = pathString.replace("//", "/");
-        }
-        if(pathString.lastIndexOf("/") == (pathString.length() - 1)) {
-            pathString = pathString.substring(0, pathString.length() - 1);
-        }
-        return pathString;
-    }
-
-    public static String removeExtension(String pathString) {
-        int extensionStartIndex = pathString.lastIndexOf(".");
-        if(extensionStartIndex != -1) {
-            return pathString.substring(0, extensionStartIndex);
-        } else {
-            return pathString;
-        }
-    }
-    
-    public static FilesystemPath removeCommonParent(FilesystemPath path, FilesystemPath pathComparedTo) {
-    	FilesystemPath outputPath = path;
-    	FilesystemPath parallelPath = pathComparedTo;
-    	
-    	while(outputPath.size() > 0 && parallelPath.size() > 0 && outputPath.get(0).equals(parallelPath.get(0))) {
-    		outputPath = outputPath.removeFirst();
-    		parallelPath = parallelPath.removeFirst();
-    	}
-    	
-    	return outputPath;
-    }
-}

File src/java/com/quigley/filesystem/FilesystemPathEditor.java

-package com.quigley.filesystem;
-
-import java.beans.PropertyEditorSupport;
-
-public class FilesystemPathEditor extends PropertyEditorSupport {
-	public void setAsText(String text) {
-		FilesystemPath path = new FilesystemPath(text);
-		setValue(path);
-	}
-}

File src/java/com/quigley/filesystem/FilesystemSizeFormatter.java

-package com.quigley.filesystem;
-
-import java.text.DecimalFormat;
-
-public class FilesystemSizeFormatter {
-	public static String format(long sizeInBytes) {
-		if(sizeInBytes < KILOBYTE) {
-			return integerFormatter.format(sizeInBytes);
-		}
-		
-		if(sizeInBytes < MEGABYTE) {
-			float kilobytes = ((float) sizeInBytes) / ((float) KILOBYTE);
-			float remainder = ((float) sizeInBytes) % ((float) KILOBYTE);
-			if(remainder == 0.0F) {
-				return integerFormatter.format(kilobytes) + " KiB";
-			} else {
-				return formatter.format(kilobytes) + " KiB";
-			}
-		}
-
-		if(sizeInBytes < GIGABYTE) {
-			float megabytes = ((float) sizeInBytes) / ((float) MEGABYTE);
-			float remainder = ((float) sizeInBytes) % ((float) MEGABYTE);
-			if(remainder == 0.0F) {
-				return integerFormatter.format(megabytes) + " MiB";
-			} else {
-				return formatter.format(megabytes) + " MiB";
-			}
-		}
-		
-		if(sizeInBytes < TERABYTE) {
-			float gigabytes = ((float) sizeInBytes) / ((float) GIGABYTE);
-			float remainder = ((float) sizeInBytes) % ((float) GIGABYTE);
-			if(remainder == 0.0F) {
-				return integerFormatter.format(gigabytes) + " GiB";
-			} else {
-				return formatter.format(gigabytes) + " GiB";
-			}
-		}
-		
-		if(sizeInBytes < PETABYTE) {
-			float terabytes = sizeInBytes / TERABYTE;
-			float remainder = sizeInBytes % TERABYTE;
-			if(remainder == 0.0F) {
-				return integerFormatter.format(terabytes) + " TiB";
-			} else {
-				return formatter.format(terabytes) + " TiB";
-			}
-		}
-		
-		float petabytes = sizeInBytes / PETABYTE;
-		float remainder = sizeInBytes % PETABYTE;
-		if(remainder == 0.0F) {
-			return integerFormatter.format(petabytes) + " PiB";
-		} else {
-			return formatter.format(petabytes) + " PiB";
-		}
-	}
-	
-	public static final long KILOBYTE = 1024L;
-	public static final long MEGABYTE = KILOBYTE * 1024L;
-	public static final long GIGABYTE = MEGABYTE * 1024L;
-	public static final long TERABYTE = GIGABYTE * 1024L;
-	public static final long PETABYTE = TERABYTE * 1024L;
-	
-	private static DecimalFormat formatter = new DecimalFormat("#####.00");
-	private static DecimalFormat integerFormatter = new DecimalFormat("#####");
-}

File src/java/com/quigley/filesystem/FilesystemVisitor.java

-package com.quigley.filesystem;
-
-public interface FilesystemVisitor {
-    public void visit(FilesystemPath path);
-}

File src/java/com/quigley/filesystem/Version.java

-package com.quigley.filesystem;
-
-public class Version {
-	public final static String VERSION = "Quigley Filesystem, Version 1.0.0 (Development Build)";
-	public final static String BUILT = "No build information is available for development builds.";
-	public final static String COPYRIGHT = "Copyright 2008-2011 Michael F. Quigley Jr.";
-}

File src/main/java/com/quigley/filesystem/FilesystemException.java

+/*
+    This file is part of Filesystem.
+
+    Filesystem 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 3 of 
+    the License, or (at your option) any later version.
+
+    Filesystem 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 Moose.  If not, see <http://www.gnu.org/licenses/>.
+*/
+    
+package com.quigley.filesystem;
+
+@SuppressWarnings("serial")
+public class FilesystemException extends RuntimeException {
+	public FilesystemException() {
+		super();
+	}
+	public FilesystemException(String message, Throwable cause) {
+		super(message, cause);
+	}
+	public FilesystemException(String message) {
+		super(message);
+	}
+	public FilesystemException(Throwable cause) {
+		super(cause);
+	}
+}

File src/main/java/com/quigley/filesystem/FilesystemIterator.java

+/*
+    This file is part of Filesystem.
+
+    Filesystem 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 3 of 
+    the License, or (at your option) any later version.
+
+    Filesystem 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 Moose.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package com.quigley.filesystem;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.quigley.filesystem.visitor.FilesystemVisitor;
+
+public class FilesystemIterator {
+	public FilesystemIterator() {
+		visitors = new ArrayList<FilesystemVisitor>();
+	}
+	
+    public FilesystemIterator(FilesystemVisitor visitor) {
+    	visitors = new ArrayList<FilesystemVisitor>();
+        visitors.add(visitor);
+    }
+
+    public void iterate(FilesystemPath path) throws FilesystemException {
+    	iterate(path, true);
+    }
+    
+    public void iterate(FilesystemPath path, boolean recursive) throws FilesystemException {
+    	File pathF = path.asFile();
+    	if(pathF.exists() && pathF.canRead()) {
+    		visit(path);
+    		if(pathF.isDirectory()) {
+    			File[] contents = pathF.listFiles();
+    			for(File f : contents) {
+    				FilesystemPath fPath = new FilesystemPath(path).add(f.getName());
+    				if(recursive) {
+    					iterate(fPath, recursive);
+    				} else {
+    					visit(fPath);
+    				}
+    			}
+    		}
+    		
+    	} else {
+    		throw new FilesystemException("Unable to read '" + path.toString() + "'!");
+    	}
+    }
+
+	private void visit(FilesystemPath path) {
+		if(visitors != null) {
+	    	for(FilesystemVisitor visitor : visitors) {
+	    		visitor.visit(path);
+	    	}
+		}
+    }
+
+	public List<FilesystemVisitor> getVisitors() {
+		return visitors;
+	}
+	public void setVisitors(List<FilesystemVisitor> visitors) {
+		this.visitors = visitors;
+	}
+	public void addVisitor(FilesystemVisitor visitor) {
+		visitors.add(visitor);
+	}
+
+    private List<FilesystemVisitor> visitors;
+}

File src/main/java/com/quigley/filesystem/FilesystemOperation.java

+/*
+    This file is part of Filesystem.
+
+    Filesystem 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 3 of 
+    the License, or (at your option) any later version.
+
+    Filesystem 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 Moose.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package com.quigley.filesystem;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.nio.channels.FileChannel;
+import java.util.ArrayList;
+import java.util.List;
+
+import com.quigley.filesystem.utils.StreamCopier;
+import com.quigley.filesystem.visitor.FilesystemInventoryVisitor;
+
+public class FilesystemOperation {
+	public static void copy(FilesystemPath source, FilesystemPath dest) throws FilesystemException {
+		copy(source.asFile(), dest.asFile());
+	}
+
+	public static void copy(File source, File dest) throws FilesystemException {
+		FileChannel sourceChannel = null;
+		FileChannel destChannel = null;
+		try {
+			sourceChannel = new FileInputStream(source).getChannel();
+			destChannel = new FileOutputStream(dest).getChannel();
+
+			sourceChannel.transferTo(0, sourceChannel.size(), destChannel);
+
+		} catch (Exception e) {
+			throw new FilesystemException("Unable to copy", e);
+		} finally {
+			if(sourceChannel != null) {
+				try {
+					sourceChannel.close();
+				} catch(IOException ioe) {
+				}
+			}
+			if(destChannel != null) {
+				try {
+					destChannel.close();
+				} catch(IOException ioe) {
+				}
+			}
+		}
+	}
+	
+	public static List<FilesystemPath> copyTree(FilesystemPath sourceTree, FilesystemPath destTree) {
+		return copyTree(sourceTree, destTree, null, null);
+	}
+	
+	public static List<FilesystemPath> copyTree(FilesystemPath sourceTree, FilesystemPath destTree, List<String> includeTokens, List<String> excludeTokens) throws FilesystemException {
+		FilesystemInventoryVisitor visitor = new FilesystemInventoryVisitor();
+		visitor.setExcludeTokens(excludeTokens);
+		visitor.setIncludeTokens(includeTokens);
+		visitor.setRootTrim(sourceTree);
+		visitor.setIncludeDirectories(false);
+		FilesystemIterator iterator = new FilesystemIterator(visitor);
+		iterator.iterate(sourceTree);
+		
+		List<FilesystemPath> copiedFiles = new ArrayList<FilesystemPath>();
+		for(FilesystemPath sourcePath : visitor.getPaths()) {
+			copiedFiles.add(sourcePath);
+			FilesystemPath destPath = destTree.add(sourcePath);
+			destPath.parent().asFile().mkdirs();
+			copy(sourceTree.add(sourcePath), destPath);
+		}
+		
+		return copiedFiles;
+	}
+
+	public static String readFileAsString(File path) throws IOException {
+		FileInputStream in = new FileInputStream(path);
+		ByteArrayOutputStream out = new ByteArrayOutputStream();
+		StreamCopier.copy(in, out);
+		in.close();
+		out.close();
+		return new String(out.toByteArray());
+	}
+	
+	public static void writeStringToFile(String data, File path) throws IOException {
+		ByteArrayInputStream in = new ByteArrayInputStream(data.getBytes());
+		FileOutputStream out = new FileOutputStream(path);
+		StreamCopier.copy(in, out);
+		in.close();
+		out.close();
+	}
+	
+	public static boolean recursiveDelete(FilesystemPath path) {
+		return recursiveDelete(path.asFile());
+	}
+	
+	public static boolean recursiveDelete(File path) {
+		if(path.exists()) {
+			if(path.isDirectory()) {
+				for(String child : path.list()) {
+					if(!recursiveDelete(new File(path, child))) {
+						return false;
+					}
+				}
+			}
+			return path.delete();
+		}
+		return false;
+	}
+}

File src/main/java/com/quigley/filesystem/FilesystemPath.java

+/*
+    This file is part of Filesystem.
+
+    Filesystem 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 3 of 
+    the License, or (at your option) any later version.
+
+    Filesystem 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 Moose.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package com.quigley.filesystem;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.LinkedList;
+import java.util.List;
+
+public class FilesystemPath implements Comparable<FilesystemPath> {
+	
+	/*
+	 * Constructors
+	 */
+	
+    public FilesystemPath(String pathString) {
+        pathString = FilesystemPath.normalize(pathString);
+        if(pathString.length() == 0) {
+        	isAbsolute = true;
+        	elements = new ArrayList<String>();
+        } else
+        if(pathString.substring(0, 1).equals("/")) {
+            isAbsolute = true;
+            pathString = pathString.substring(1, pathString.length());
+        }
+        if(pathString.length() > 0) {
+	        String[] componentArray = pathString.split("/");
+	        elements = new ArrayList<String>(componentArray.length);
+	        for(String component : componentArray) {
+	            elements.add(component);
+	        }
+        }
+    }
+
+    public FilesystemPath(FilesystemPath source) {
+        elements = new LinkedList<String>();
+        for(int i = 0; i < source.size(); i++) {
+            elements.add(source.get(i));
+        }
+        isAbsolute = source.isAbsolute();
+    }
+    
+    public FilesystemPath(List<String> elements) {
+    	this.elements = elements;
+    }
+    
+    /*
+     * Absolute
+     */
+
+    public FilesystemPath toAbsolute() {
+    	FilesystemPath absolutePath = new FilesystemPath(this.asFile().getAbsolutePath());
+    	if(absolutePath.getLast().equals(".")) {
+    		absolutePath = absolutePath.removeLast();
+    	}
+    	return absolutePath;
+    }
+    
+    public boolean isAbsolute() {
+        return isAbsolute;
+    }
+    public void setAbsolute(boolean absolute) {
+        isAbsolute = absolute;
+    }
+
+    /*
+     * Modifiers
+     */
+    
+    public FilesystemPath add(String element) {
+    	List<String> elementsCopy = new ArrayList<String>(elements);
+    	
+        elementsCopy.add(element);
+        
+        FilesystemPath pathCopy = new FilesystemPath(elementsCopy);
+        pathCopy.setAbsolute(isAbsolute);
+        
+        return pathCopy;
+    }
+    
+    public FilesystemPath add(FilesystemPath p) {
+    	List<String> elementsCopy = new ArrayList<String>(elements);
+    	
+        for(int i = 0; i < p.size(); i++) {
+            elementsCopy.add(p.get(i));
+        }
+
+        FilesystemPath pathCopy = new FilesystemPath(elementsCopy);
+        pathCopy.setAbsolute(isAbsolute);
+        
+        return pathCopy;
+    }
+
+    public FilesystemPath set(int idx, String component) {
+    	List<String> elementsCopy = new ArrayList<String>(elements);
+    	
+        elementsCopy.set(idx, component);
+
+        FilesystemPath pathCopy = new FilesystemPath(elementsCopy);
+        pathCopy.setAbsolute(isAbsolute);
+        
+        return pathCopy;
+    }    
+    
+    public FilesystemPath remove(int idx) {
+    	List<String> elementsCopy = new ArrayList<String>(elements);
+    	
+        elementsCopy.remove(idx);
+        
+        FilesystemPath pathCopy = new FilesystemPath(elementsCopy);
+        pathCopy.setAbsolute(isAbsolute);
+        
+        return pathCopy;
+    }
+    
+    public FilesystemPath removeFirst() {
+    	List<String> elementsCopy = new ArrayList<String>(elements);
+    	
+    	elementsCopy.remove(0);
+    	
+    	FilesystemPath pathCopy = new FilesystemPath(elementsCopy);
+    	
+    	return pathCopy;
+    }
+    
+    public FilesystemPath removeFirst(int count) {
+    	List<String> elementsCopy = new ArrayList<String>(elements);
+    	
+    	elementsCopy = elementsCopy.subList(count, elementsCopy.size());
+    	
+    	FilesystemPath pathCopy = new FilesystemPath(elementsCopy);
+    	
+    	return pathCopy;
+    }
+    
+    public FilesystemPath removeLast() {
+    	List<String> elementsCopy = new ArrayList<String>(elements);
+    	
+    	elementsCopy.remove(elements.size() - 1);
+    	
+    	FilesystemPath pathCopy = new FilesystemPath(elementsCopy);
+    	pathCopy.setAbsolute(isAbsolute);
+    	
+    	return pathCopy;
+    }
+    
+    public FilesystemPath setLast(String last) {
+    	List<String> elementsCopy = new ArrayList<String>(elements);
+    	
+    	if(elementsCopy.size() > 0) {
+    		elementsCopy.set(elementsCopy.size() - 1, last);
+    		
+    	} else {
+    		elementsCopy.add(last);
+    	}
+    	
+    	FilesystemPath pathCopy = new FilesystemPath(elementsCopy);
+    	pathCopy.setAbsolute(isAbsolute);
+    	
+    	return pathCopy;
+    }
+    
+    public FilesystemPath removeCommonParent(FilesystemPath otherPath) {
+    	FilesystemPath outputPath = this;
+    	FilesystemPath parallelPath = otherPath;
+    	
+    	while(outputPath.size() > 0 && parallelPath.size() > 0 && outputPath.get(0).equals(parallelPath.get(0))) {
+    		outputPath = outputPath.removeFirst();
+    		parallelPath = parallelPath.removeFirst();
+    	}
+    	
+    	return outputPath;
+    }
+
+    /*
+     * Component Accessors
+     */
+    
+    public FilesystemPath parent() {
+    	return removeLast();
+    }
+
+    public int size() {
+        return elements.size();
+    }    
+    
+    public String get(int index) {
+        return elements.get(index);
+    }
+    
+    public String getLast() {
+    	if(size() > 0) {
+    		return elements.get(elements.size() - 1);
+    		
+    	} else {
+    		return null;
+    	}
+    }
+
+    /*
+     * Extension
+     */
+    
+    public String getExtension() {
+        if(elements.size() < 1) {
+            return null;
+        }
+        String element = elements.get(elements.size() - 1);
+        int extStart = element.lastIndexOf(".");
+        if(extStart != -1) {
+            return element.substring(extStart + 1);
+        } else {
+            return null;
+        }
+    }    
+    
+    public FilesystemPath removeExtension() {
+    	return new FilesystemPath(removeExtension(this.toString()));
+    }
+    
+    public FilesystemPath setExtension(String extension) {
+    	FilesystemPath pathCopy = new FilesystemPath(removeExtension(this.toString()));
+    	pathCopy = new FilesystemPath(pathCopy.toString() + "." + extension);
+    	return pathCopy;
+    }
+    
+    public FilesystemPath addExtension(String extension) {
+    	return new FilesystemPath(this.toString() + "." + extension);
+    }
+    
+    /*
+     * Matching 
+     */
+    
+    public boolean contains(String match) {
+    	for(String element : elements) {
+    		if(element.equals(match)) {
+    			return true;
+    		}
+    	}
+    	return false;
+    }
+    
+
+    public File asFile() {
+        return new File(this.toString());
+    }
+    
+    @Override
+	public int compareTo(FilesystemPath arg0) {
+		return toString().compareTo(arg0.toString());
+	}
+
+	@Override
+	public int hashCode() {
+		final int prime = 31;
+		int result = 1;
+		result = prime * result
+				+ ((elements == null) ? 0 : elements.hashCode());
+		result = prime * result + (isAbsolute ? 1231 : 1237);
+		return result;
+	}
+
+	@Override
+	public boolean equals(Object obj) {
+		if (this == obj)
+			return true;
+		if (obj == null)
+			return false;
+		if (getClass() != obj.getClass())
+			return false;
+		final FilesystemPath other = (FilesystemPath) obj;
+		if (elements == null) {
+			if (other.elements != null)
+				return false;
+		} else if (!elements.equals(other.elements))
+			return false;
+		if (isAbsolute != other.isAbsolute)
+			return false;
+		return true;
+	}
+
+	public String toString() {
+        StringBuilder sb = new StringBuilder();
+        if(isAbsolute && elements.size() == 0) {
+        	sb.append("/");
+        } else {
+	        for(int i = 0; i < elements.size(); i++) {
+	            if(isAbsolute || i > 0) {
+	                sb.append("/");
+	            }
+	            sb.append(elements.get(i));
+	        }
+        }
+        return sb.toString();
+    }
+
+    private boolean isAbsolute;
+    private List<String> elements;
+	
+    /*
+     * Static Operations
+     */
+    
+    public static String normalize(String pathString) {
+        while(pathString.indexOf("\\") != -1) {
+            pathString = pathString.replace('\\', '/');
+        }
+        while(pathString.indexOf("//") != -1) {
+            pathString = pathString.replace("//", "/");
+        }
+        if(pathString.lastIndexOf("/") == (pathString.length() - 1)) {
+            pathString = pathString.substring(0, pathString.length() - 1);
+        }
+        return pathString;
+    }
+
+    private static String removeExtension(String pathString) {
+        int extensionStartIndex = pathString.lastIndexOf(".");
+        if(extensionStartIndex != -1) {
+            return pathString.substring(0, extensionStartIndex);
+        } else {
+            return pathString;
+        }
+    }
+    
+    public static List<FilesystemPath> removeCommonParent(List<FilesystemPath> paths, FilesystemPath otherPath) {
+    	List<FilesystemPath> trimmedPaths = new ArrayList<FilesystemPath>();
+    	for(FilesystemPath path : paths) {
+    		trimmedPaths.add(path.removeCommonParent(otherPath));
+    	}
+    	return trimmedPaths;
+    }
+}

File src/main/java/com/quigley/filesystem/FilesystemPathEditor.java

+/*
+    This file is part of Filesystem.
+
+    Filesystem 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 3 of 
+    the License, or (at your option) any later version.
+
+    Filesystem 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 Moose.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package com.quigley.filesystem;
+
+import java.beans.PropertyEditorSupport;
+
+public class FilesystemPathEditor extends PropertyEditorSupport {
+	public void setAsText(String text) {
+		FilesystemPath path = new FilesystemPath(text);
+		setValue(path);
+	}
+}

File src/main/java/com/quigley/filesystem/utils/FilesystemSizeFormatter.java

+/*
+    This file is part of Filesystem.
+
+    Filesystem 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 3 of 
+    the License, or (at your option) any later version.
+
+    Filesystem 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 Moose.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package com.quigley.filesystem.utils;
+
+import java.text.DecimalFormat;
+
+public class FilesystemSizeFormatter {
+	public static String format(long sizeInBytes) {
+		if(sizeInBytes < KILOBYTE) {
+			return integerFormatter.format(sizeInBytes);
+		}
+		
+		if(sizeInBytes < MEGABYTE) {
+			float kilobytes = ((float) sizeInBytes) / ((float) KILOBYTE);
+			float remainder = ((float) sizeInBytes) % ((float) KILOBYTE);
+			if(remainder == 0.0F) {
+				return integerFormatter.format(kilobytes) + " KiB";
+			} else {
+				return formatter.format(kilobytes) + " KiB";
+			}
+		}
+
+		if(sizeInBytes < GIGABYTE) {
+			float megabytes = ((float) sizeInBytes) / ((float) MEGABYTE);
+			float remainder = ((float) sizeInBytes) % ((float) MEGABYTE);
+			if(remainder == 0.0F) {
+				return integerFormatter.format(megabytes) + " MiB";
+			} else {
+				return formatter.format(megabytes) + " MiB";
+			}
+		}
+		
+		if(sizeInBytes < TERABYTE) {
+			float gigabytes = ((float) sizeInBytes) / ((float) GIGABYTE);
+			float remainder = ((float) sizeInBytes) % ((float) GIGABYTE);
+			if(remainder == 0.0F) {
+				return integerFormatter.format(gigabytes) + " GiB";
+			} else {
+				return formatter.format(gigabytes) + " GiB";
+			}
+		}
+		
+		if(sizeInBytes < PETABYTE) {
+			float terabytes = sizeInBytes / TERABYTE;
+			float remainder = sizeInBytes % TERABYTE;
+			if(remainder == 0.0F) {
+				return integerFormatter.format(terabytes) + " TiB";
+			} else {
+				return formatter.format(terabytes) + " TiB";
+			}
+		}
+		
+		float petabytes = sizeInBytes / PETABYTE;
+		float remainder = sizeInBytes % PETABYTE;
+		if(remainder == 0.0F) {
+			return integerFormatter.format(petabytes) + " PiB";
+		} else {
+			return formatter.format(petabytes) + " PiB";
+		}
+	}
+	
+	public static final long KILOBYTE = 1024L;
+	public static final long MEGABYTE = KILOBYTE * 1024L;
+	public static final long GIGABYTE = MEGABYTE * 1024L;
+	public static final long TERABYTE = GIGABYTE * 1024L;
+	public static final long PETABYTE = TERABYTE * 1024L;
+	
+	private static DecimalFormat formatter = new DecimalFormat("#####.00");
+	private static DecimalFormat integerFormatter = new DecimalFormat("#####");
+}

File src/main/java/com/quigley/filesystem/utils/StreamCopier.java

+/*
+    This file is part of Filesystem.
+
+    Filesystem 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 3 of 
+    the License, or (at your option) any later version.
+
+    Filesystem 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 Moose.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package com.quigley.filesystem.utils;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+
+public class StreamCopier {
+	public static void copy(InputStream in, OutputStream out) throws IOException {
+		synchronized(in) {
+			synchronized(out) {
+				byte[] buffer = new byte[4096];
+				while(true) {
+					int length = in.read(buffer);
+					if(length == -1) {
+						break;
+					}
+					out.write(buffer, 0, length);
+				}
+			}
+		}
+	}
+}

File src/main/java/com/quigley/filesystem/visitor/FilesystemInventoryVisitor.java

+/*
+    This file is part of Filesystem.
+
+    Filesystem 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 3 of 
+    the License, or (at your option) any later version.
+
+    Filesystem 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 Moose.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package com.quigley.filesystem.visitor;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.quigley.filesystem.FilesystemPath;
+
+public class FilesystemInventoryVisitor implements FilesystemVisitor {
+	public FilesystemInventoryVisitor() {
+		paths = new ArrayList<FilesystemPath>();
+		includeFiles = true;
+		includeDirectories = true;
+		excludeTokens = new ArrayList<String>();
+		includeTokens = new ArrayList<String>();
+	}
+	
+	public void visit(FilesystemPath path) {
+		boolean include = true;
+		if(includeTokens != null && includeTokens.size() > 0) {
+			include = false;
+			for(String includeToken : includeTokens) {
+				if(path.toString().indexOf(includeToken) != -1) {
+					include = true;
+					break;
+				}
+			}
+		}
+		boolean exclude = false;
+		if(excludeTokens != null && excludeTokens.size() > 0) {
+			for(String excludeToken : excludeTokens) {
+				if(path.toString().indexOf(excludeToken) != -1) {
+					exclude = true;
+					break;
+				}
+			}
+		}
+		if(include && !exclude) {
+			if((path.asFile().isDirectory() && includeDirectories) || (path.asFile().isFile() && includeFiles)) {
+				if(rootTrim == null) {
+					paths.add(path);
+				} else {
+					paths.add(path.removeCommonParent(rootTrim));
+				}
+			}
+		}
+	}
+	
+	public boolean isIncludeDirectories() {
+		return includeDirectories;
+	}
+	public void setIncludeDirectories(boolean includeDirectories) {
+		this.includeDirectories = includeDirectories;
+	}
+	
+	public boolean isIncludeFiles() {
+		return includeFiles;
+	}
+	public void setIncludeFiles(boolean includeFiles) {
+		this.includeFiles = includeFiles;
+	}
+
+	public List<String> getExcludeTokens() {
+		return excludeTokens;
+	}
+	public void setExcludeTokens(List<String> excludeTokens) {
+		if(excludeTokens != null) {
+			this.excludeTokens = excludeTokens;
+		}
+	}
+	public void addExcludeToken(String excludeToken) {
+		excludeTokens.add(excludeToken);
+	}
+
+	public List<String> getIncludeTokens() {
+		return includeTokens;
+	}
+	public void setIncludeTokens(List<String> includeTokens) {
+		if(includeTokens != null) {
+			this.includeTokens = includeTokens;
+		}
+	}
+	public void addIncludeToken(String includeToken) {
+		includeTokens.add(includeToken);
+	}
+
+	public FilesystemPath getRootTrim() {
+		return rootTrim;
+	}
+	public void setRootTrim(FilesystemPath rootTrim) {
+		this.rootTrim = rootTrim;
+	}
+
+	public List<FilesystemPath> getPaths() {
+		return paths;
+	}
+
+	protected boolean includeDirectories;
+	protected boolean includeFiles;
+	protected List<String> excludeTokens;
+	protected List<String> includeTokens;
+	protected FilesystemPath rootTrim;
+	
+	protected List<FilesystemPath> paths;
+}

File src/main/java/com/quigley/filesystem/visitor/FilesystemModificationVisitor.java

+/*
+    This file is part of Filesystem.
+
+    Filesystem 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 3 of 
+    the License, or (at your option) any later version.
+
+    Filesystem 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 Moose.  If not, see <http://www.gnu.org/licenses/>.
+*/
+
+package com.quigley.filesystem.visitor;
+
+import com.quigley.filesystem.FilesystemPath;
+
+public class FilesystemModificationVisitor implements FilesystemVisitor {
+	public void visit(FilesystemPath path) {
+		long fileModified = path.asFile().lastModified();