On Mon, Mar 2, 2009 at 4:54 PM, Niklas Gustavsson <[email protected]> wrote:
> Let me whip up a prototype and we can test it on the available platforms.
Alright, here's a prototype that I would much appreciate if all of you
would take the time to test. First of all, you need to set the
FILE_SYSTEM_TYPE to the correct value (this is only used for
verification in the tests). On case sensitive file systems (most of
the *nix file systems), this should be set to
FileSystemType.CaseSensitive. On NTFS it should be set to
CaseInsensitiveMissingAsTrue, and on case-insensitive HFS to
CaseInsensitiveMissingAsFalse. It would be very interesting to have
this code tested on other file systems. If you do, please report the
file system and the test results.
/niklas
package org.apache.ftpserver.filesystem.nativefs.impl;
import java.io.File;
import java.io.FilenameFilter;
import java.io.IOException;
import junit.framework.TestCase;
public class FileEqualsTest extends TestCase {
private enum FileSystemType {
CaseSensitive,
// non-existing files with different casing are equal
// e.g. NTFS
CaseInsensitiveMissingAsTrue,
// non-existing files with different casing are non-equal
// e.g. HFS
CaseInsensitiveMissingAsFalse
};
private static final FileSystemType FILE_SYSTEM_TYPE =
FileSystemType.CaseSensitive;
private boolean isCaseSensitive() {
return FILE_SYSTEM_TYPE == FileSystemType.CaseSensitive;
}
private boolean isCaseInsensitive() {
return FILE_SYSTEM_TYPE != FileSystemType.CaseSensitive;
}
@Override
protected void setUp() throws Exception {
System.out.println("File system type: " + FILE_SYSTEM_TYPE);
super.setUp();
}
public void testSimple() throws IOException {
File f1 = new File("foo");
assertTrue(f1.createNewFile());
File f2 = new File("foo");
assertTrue(equals(f1, f2));
}
public void testNonExistingButSimple() {
File f1 = new File("foo");
File f2 = new File("foo");
assertTrue(equals(f1, f2));
}
public void testDifferentCase() throws IOException {
File f1 = new File("foo");
assertTrue(f1.createNewFile());
File f2 = new File("FOO");
// if this succeeds, we have two different files
assertEquals(isCaseSensitive(), f2.createNewFile());
assertEquals(isCaseInsensitive(), equals(f1, f2));
}
public void testDifferentCaseNonExisting() throws IOException {
File f1 = new File("foo");
File f2 = new File("FOO");
assertEquals(FILE_SYSTEM_TYPE ==
FileSystemType.CaseInsensitiveMissingAsTrue, equals(f1, f2));
}
public void testSameNameDifferentParent() throws IOException {
File f1 = new File("foo");
assertTrue(f1.createNewFile());
File dir2 = new File("dir");
assertTrue(dir2.mkdir());
File f2 = new File(dir2, "foo");
assertTrue(f2.createNewFile());
assertFalse(equals(f1, f2));
}
public boolean equals(final File f1, final File f2) {
if (f1 == null || f2 == null) {
return false;
}
if (f1.equals(f2)) {
// file equals, valid on all file systems
return true;
} else {
boolean f1Exists = f1.exists();
boolean f2Exists = f2.exists();
if (f1.getName().equalsIgnoreCase(f2.getName())) {
// same name, do both exist?
if (f1Exists && f2Exists) {
// both files exists, do they have the same parent?
// TODO should we use canonical or absolute path?
File f1Parent = f1.getAbsoluteFile().getParentFile();
File f2Parent = f2.getAbsoluteFile().getParentFile();
if (f1Parent != null && f2Parent != null) {
if (equals(f1Parent, f2Parent)) {
// okay, if we got here, the parents are the same,
// the file names are the same and both files exists
// let's check if there are multiple files
or only one in the parent
// with that name
// choosing either parent should be fine,
they are equal
String[] files = f1Parent
.list(new FilenameFilter() {
public boolean accept(File
dir, String name) {
return name.equalsIgnoreCase(f1
.getName());
}
});
if (files.length == 1) {
// exactly one file matched the name,
the files must be equal
return true;
} else if (files.length > 1) {
return false;
} else {
// no file matching, must be a bug somewhere
throw new RuntimeException("Should not happen");
}
} else {
// different parents, return false
return false;
}
} else if (f1Parent == null && f2Parent == null) {
// no parents, we can do nothing more and thus
have to return false
return false;
} else {
// different parents
return false;
}
} else if (!f1Exists && !f2Exists) {
// none of the file exists, we can only trust the
equals result (which by now must be false)
return false;
} else {
// only one of the files exists, return false
return false;
}
} else {
// file names not equal
return false;
}
}
}
@Override
protected void tearDown() throws Exception {
new File("foo").delete();
new File("FOO").delete();
new File("dir/foo").delete();
new File("dir").delete();
}
}