On Mon, Mar 2, 2009 at 4:54 PM, Niklas Gustavsson <nik...@protocol7.com> 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();
    }

}

Reply via email to