4

I have an application using JFileChooser to select a directory. In this app, I do not want to try to process symbolic links, and I would like to run on multiple platforms. The code therefore attempts to determine whether a chosen file is a symbolic link, and displays an error dialog if it is.

Here is the code that obtains the File from the JFileChooser.

public File getDirectoryChoice(String buttonText, String currentDirectory)
{
  File chosenFile = null;
  if (fileChooser == null) { fileChooser = new JFileChooser(); }
  if (currentDirectory != null) 
    { fileChooser.setCurrentDirectory(new File(currentDirectory)); }
  fileChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
  fileChooser.setApproveButtonText(buttonText);
  int returnValue = fileChooser.showOpenDialog(mainFrame);
  if (returnValue == JFileChooser.APPROVE_OPTION)
  {
    chosenFile = fileChooser.getSelectedFile();
  }
  return chosenFile;
}

and here is the code I use to determine whether the chosen file is a symbolic link:

public static boolean isSymbolic(File f)
{
  try
  { 
    String absolute = f.getAbsolutePath();
    String canonical = f.getCanonicalPath();
    return !(absolute.equals(canonical));
  }
  catch (IOException ioe)
  {
    return false;
  }
}

On Windows 7: If the user chooses a given directory using the mouse, this works fine. If the user types the same directory name into the filename text box, the second code snippet indicates that the absolute and canonical paths are not the same. It does not matter if the user enters the trailing backslash or not.

When I stop this in the debugger on the 'return' statement line and look at the details of the two strings, the hash value of the absolute path string is a big negative number, and the hash value of the canonical string is 0. I don't know why that would be, and in fact wonder if it's a quirk of the (eclipse) debugger.

Can anyone tell me why there would be this difference?

arcy
  • 12,845
  • 12
  • 58
  • 103

2 Answers2

2

Double check that you are typing the string exactly the same as what is returned by JFileChooser.

I tried this out and without thinking I typed c:\temp. Just like in your tests, isSymbolic() method returned true. However upon closer inspection, I noticed that when I selected this path in the file chooser, it returned C:\temp instead (note the capital C).

So even though it's ugly, you could add a special case something like this:

    if (System.getProperty("os.name").toLowerCase().indexOf("win") >= 0)
        return !(absolute.equalsIgnoreCase(canonical));
    else
        return !(absolute.equals(canonical));

You also might want to look at this related question: "Java 1.6 - determine symbolic links". There are quite a few answers there which you might find helpful.

Community
  • 1
  • 1
Jason Braucht
  • 2,358
  • 19
  • 31
  • Capital C -- doesn't matter how much experience you have, you can still get caught by that (evidently). – arcy Apr 23 '12 at 21:35
1

If you add absolute.hashCode() and canonical.hashCode() to the Expressions window in Eclipe you should see the correct values - both not zero. String has hash member that is initialized to 0. Then it is updated to the correct value when hashCode() is executed.

So what you probably see is the calculated hash for absolute string and not yet calculated hash for canonical string.

Put a breakpoint in String.hashCode() and you will see that getCanonicalPath() on Windows triggers absolute.hashCode(). It appears that implementation of getCanonicalPath() on Windows caches results of canonicalization. Somewhere in between of WinNTFileSystem.canonicalize(), the lookup in the cached results map triggers absolute.hashCode().

So at the breakpoint that you set on return statement in isSymbolic(), absolute has a valid hash code and canonical has it still as zero.

Here is the stack trace that demonstrates absolute.hashCode() execution as a result of f.getCanonicalPath() :

String.hashCode() line: 1482 [local variables unavailable]  
ExpiringCache$1(HashMap<K,V>).getEntry(Object) line: 344    
ExpiringCache$1(LinkedHashMap<K,V>).get(Object) line: 280   
ExpiringCache.entryFor(String) line: 83 
ExpiringCache.get(String) line: 58  
WinNTFileSystem(Win32FileSystem).canonicalize(String) line: 377 
File.getCanonicalPath() line: 559   
Test.isSymbolic(File) line: 25  
Test.main(String[]) line: 16
tenorsax
  • 21,123
  • 9
  • 60
  • 107