JDK - GetAbsolutePath doesn't

I just finished fixing a bug and I decided to share it so you can avoid the misery of finding it yourself.

JDK's java.io.File is not that smart about Files vs. String Paths.   You may use getAbsolutePath and getAbsoluteFile because you think it will really give you an absolute path.  Not true at all.  It merely fills in the beginning of the path for you.  It does nothing about trailing dots.

Say your current directory is /foo.  Let's say you have a file named x in the current directory.

String s = new File("x").getAbsolutePath();

this returns "/foo/x".  Perfect!

Now say you do something crazy like

 String s = new File("../foo/./x").getAbsolutePath();

this returns "/foo/./x"  -- doesn't look absolute to me!

What the JDK means by absolute is only that the absolute path will unambiguously point at a file -- without needing the location of the current directory.  Period.   It does most definitely not imply that that you are getting a path with no relative elements in it.

getCanonicalFile and getCanonicalPath are highly under-used methods in my experience.  These methods guarantee to return a 1:1 mapping with the file.  I.e. there is one and only one unique Canonical path for every file on disk.  There are an infinite number of Absolute paths for a given file.

 String s = new File("../foo/./x").getCanonicalPath();

this returns "/foo/x" -- just what you wanted.

Now here is where I got into trouble.  I'm given a ABSOLUTE  path to a jar file.  I need to get the parent directory of the directory that contains the jar file.  So I innocently did the following:

given:  File f  which internally has this path "/foo/lib/./x.jar"

 File grandparent = f.getParentFile().getParentFile();

String s = grandparent.getPath();

I expected the String to be "/foo"

It wasn't though, it was "/foo/lib"!

 JDK says the parent is "/foo/lib/.", and the parent of the parent is "/foo/lib"

JDK is not treating "." specially but, rather, like it's a regular file in this context!

My code was working perfectly until the caller decided to drop a dot in the middle of the path and then suddenly my code broke.

SOLUTION:

Use CanonicalPath and CanonicalFile.  But wait -- they might throw an IOException.  What a hassle!!

That's no big deal.  I've been using them for years and I have never seen an Exception get thrown.  JDK is paranoid because these methods call into native code to get the right path.  I've written this following snippet of code way, way too many times.  I highly recommend it -- but add it into your utility code, write it once and call it a gazillion times.
 

public static File sanitize(File f) {
    if(f == null) {
        return null;
    }
    try {
        return f.getCanonicalFile();
    }
    catch(Exception e) {
        return f.getAbsoluteFile();
   }
}

Here is some sample code you can try, along with the output:
public class Main
{
    public static void main(String[] args)
    {
        File f = new File(System.getProperty("java.io.tmpdir") + "/foo");
        f.mkdirs();
        String foo = f.getPath();
       
        foo += "/./././././.";
        f = new File(foo);
        // f is now "c:/tmp/foo/./././././."
       
        while((f = f.getParentFile()) != null) {
            System.out.println("PARENT: " + f);
        }
    }
  }

 Output:

PARENT: c:\\tmp\\foo\\.\\.\\.\\.\\.
PARENT: c:\\tmp\\foo\\.\\.\\.\\.
PARENT: c:\\tmp\\foo\\.\\.\\.
PARENT: c:\\tmp\\foo\\.\\.
PARENT: c:\\tmp\\foo\\.
PARENT: c:\\tmp\\foo
PARENT: c:\\tmp
PARENT: c:\\

 

Comments:

Great eye-opener.
Thanks.

Posted by Kedar Mhaswade on March 21, 2008 at 03:57 PM PDT #

Great work, Byron.Very interesting

Posted by Siraj Ghaffar on March 22, 2008 at 01:45 AM PDT #

Post a Comment:
  • HTML Syntax: NOT allowed
About

ByronNevins

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today