Java Solaris Communities Sun Store Join SDN My Profile Why Join?
 
Bug Database
Bug Detail
Quick Lists
Top 25 Bugs
Top 25 RFE's
Recently Closed Bugs
Printable Page Printable Page


Bug Database
Bug ID: 4906607
Votes 2
Synopsis Can't select a file through symlink using a JFileChooser
Category java:classes_swing
Reported Against 1.4.2 , mantis
Release Fixed mustang(b86)
State 10-Fix Delivered, bug
Priority: 2-High
Related Bugs 6358641 , 4199911 , 4893175 , 5077426 , 4615460
Submit Date 14-AUG-2003
Description
JFileChooser impl uses getCanonicalFile() internally during FS browsing.
If the user has a file and a symlink, e.g.:
/home/user/data/file.txt
/data -> /home/user/data,
and wants to select the file through that symlink, he can't browse there,
because the JFileChooser will convert /data to /home/user/data
There are legitimate uses for the need to get the user selected symlink
instead of the canonical path to the target, example:
The selected path may be persisted between app sessions and the user may need
to change the target of the symlink (different version of a library, some VCS uses symlinks for paths to latest valid bits, ...)



File.getCanonical* calls are not apparently made from JFileChooser itself, nor from FileSystemView, but are made from various FileChooserUI subclasses.
  xxxxx@xxxxx   2004-07-21
Work Around
User can type the full path to the name field, but this is highly uncomfortable.

-----

May be possible to work around this in client code using extreme hacks with subclassing java.io.File etc. By no means straightforward.
  xxxxx@xxxxx   2004-02-24


Have found a workaround which seems to work reliably in Metal on 1.4.2_04 and GTK on 1.5.0 b58, and nearly reliably in Ocean (Metal) on 1.5.0 b58. (In the latter case, sometimes - not always - the first time you traverse a symlink to a dir it will be canonicalized, but you can back up and do it again and it will not be. Failed completely to track down why this happens; when I add a diagnostic patch to java/io/File.java, compile that, and add that using -Xbootclasspath/p:$pathdir, the problem mysteriously does not occur at all.)

Try the following class:

---%<---
package fileChooserCanonicalPath;
import java.io.File;
import java.io.FileFilter;
import java.io.FileOutputStream;
import java.io.FilenameFilter;
import java.io.IOException;
import java.net.URI;
import javax.swing.Icon;
import javax.swing.JFileChooser;
import javax.swing.UIManager;
import javax.swing.filechooser.FileSystemView;
public class Main {
    public static void main(String[] args) throws Exception {
        File tmp = new File(System.getProperty("java.io.tmpdir"), "fileChooserCanonicalPathTest");
        del(tmp);
        File phys = new File(tmp, "physdir");
        File file = new File(phys, "file");
        File link = new File(tmp, "link");
        phys.mkdirs();
        new FileOutputStream(file).close();
        Runtime.getRuntime().exec(new String[] {"ln", "-s", "physdir", link.getAbsolutePath()});
        //UIManager.setLookAndFeel("com.sun.java.swing.plaf.gtk.GTKLookAndFeel");
        JFileChooser jfc = new JFileChooser();
        //File currdir = tmp;
        File currdir = translate(tmp);
        jfc.setCurrentDirectory(currdir);
        jfc.setFileSystemView(new WrapperFileSystemView());
        System.out.println("Try to select " + new File(link, "file").getAbsolutePath());
        jfc.showOpenDialog(null);
        System.out.println(jfc.getSelectedFile());
        System.exit(0);
    }
    private static void del(File f) throws IOException {
        if (f.isDirectory()) {
            File[] ks = f.listFiles();
            for (int i = 0; i < ks.length; i++) {
                del(ks[i]);
            }
        }
        f.delete();
    }
    private static File translate(File f) {
        if (f instanceof WrapperFile) {
            return f;
        } else if (f != null) {
            return new WrapperFile(f);
        } else {
            return null;
        }
    }
    private static File[] translate(File[] fs) {
        if (fs != null) {
            for (int i = 0; i < fs.length; i++) {
                fs[i] = translate(fs[i]);
            }
        }
        return fs;
    }
    private static File normalize(File f) {
        if (f.getAbsolutePath().equals("/..")) {
            return new File("/");
        } else {
            return new File(f.toURI().normalize());
        }
    }
    private static final class WrapperFile extends File {
        public WrapperFile(File orig) {
            this(orig.getPath());
        }
        private WrapperFile(String path) {
            super(path);
        }
        private WrapperFile(URI uri) {
            super(uri);
        }
        public File getCanonicalFile() throws IOException {
            return translate(normalize(this));
        }
        public String getCanonicalPath() throws IOException {
            return normalize(this).getAbsolutePath();
        }
        public File getParentFile() {
            return translate(super.getParentFile());
        }
        public File getAbsoluteFile() {
            return translate(super.getAbsoluteFile());
        }
        public File[] listFiles() {
            return translate(super.listFiles());
        }
        public File[] listFiles(FileFilter filter) {
            return translate(super.listFiles(filter));
        }
        public File[] listFiles(FilenameFilter filter) {
            return translate(super.listFiles(filter));
        }
    }
    private static final class WrapperFileSystemView extends FileSystemView {
        private final FileSystemView delegate = FileSystemView.getFileSystemView();
        public WrapperFileSystemView() {}
        public boolean isFloppyDrive(File dir) {
            return delegate.isFloppyDrive(dir);
        }
        public boolean isComputerNode(File dir) {
            return delegate.isComputerNode(dir);
        }
        public File createNewFolder(File containingDir) throws IOException {
            return translate(delegate.createNewFolder(containingDir));
        }
        public boolean isDrive(File dir) {
            return delegate.isDrive(dir);
        }
        public boolean isFileSystemRoot(File dir) {
            return delegate.isFileSystemRoot(dir);
        }
        public File getHomeDirectory() {
            return translate(delegate.getHomeDirectory());
        }
        public File createFileObject(File dir, String filename) {
            return translate(delegate.createFileObject(dir, filename));
        }
        public Boolean isTraversable(File f) {
            return delegate.isTraversable(f);
        }
        public boolean isFileSystem(File f) {
            return delegate.isFileSystem(f);
        }
        /*
        protected File createFileSystemRoot(File f) {
            return translate(delegate.createFileSystemRoot(f));
        }
         */
        public File getChild(File parent, String fileName) {
            return translate(delegate.getChild(parent, fileName));
        }
        public File getParentDirectory(File dir) {
            return translate(delegate.getParentDirectory(dir));
        }
        public Icon getSystemIcon(File f) {
            return delegate.getSystemIcon(f);
        }
        public boolean isParent(File folder, File file) {
            return delegate.isParent(folder, file);
        }
        public String getSystemTypeDescription(File f) {
            return delegate.getSystemTypeDescription(f);
        }
        public File getDefaultDirectory() {
            return translate(delegate.getDefaultDirectory());
        }
        public String getSystemDisplayName(File f) {
            return delegate.getSystemDisplayName(f);
        }
        public File[] getRoots() {
            return translate(delegate.getRoots());
        }
        public boolean isHiddenFile(File f) {
            return delegate.isHiddenFile(f);
        }
        public File[] getFiles(File dir, boolean useFileHiding) {
            return translate(delegate.getFiles(dir, useFileHiding));
        }
        public boolean isRoot(File f) {
            return delegate.isRoot(f);
        }
        public File createFileObject(String path) {
            return translate(delegate.createFileObject(path));
        }
    }
}
---%<---

If you comment out the call to setFileSystemView (and use "File currdir = tmp;") the bug is clearly manifested on Linux: though you try to select .../link/file you wind up selecting .../physdir/file. With the code as written it usually works; you can select .../link/file. You can uncomment the call to UIManager to test on GTK as well.
  xxxxx@xxxxx   2004-07-21
Evaluation
Will investigate to see if the getCanonicalPath() method can be modified for
JFileChooser.
  xxxxx@xxxxx   2003-09-30
Two stack traces that will help in evaluation:
I'm creating a JFileChooser with a currentDirectory parameter that is a symbolic link.

MetalFileChooserUI.DirectoryComboboxModel canonicalize file ...

        at javax.swing.plaf.metal.MetalFileChooserUI$DirectoryComboBoxModel.addItem(MetalFileChooserUI.java:942)
        at javax.swing.plaf.metal.MetalFileChooserUI$DirectoryComboBoxModel.access$800(MetalFileChooserUI.java:900)
        at javax.swing.plaf.metal.MetalFileChooserUI.doDirectoryChanged(MetalFileChooserUI.java:651)
        at javax.swing.plaf.metal.MetalFileChooserUI.access$1100(MetalFileChooserUI.java:36)
        at javax.swing.plaf.metal.MetalFileChooserUI$5.propertyChange(MetalFileChooserUI.java:737)
        at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:338)
        at java.beans.PropertyChangeSupport.firePropertyChange(PropertyChangeSupport.java:275)
        at java.awt.Component.firePropertyChange(Component.java:7688)
        at javax.swing.JFileChooser.setCurrentDirectory(JFileChooser.java:563)
        at javax.swing.JFileChooser.<init>(JFileChooser.java:333)
        at javax.swing.JFileChooser.<init>(JFileChooser.java:315)


... and sets it to JFileChooser instance:


        at javax.swing.JFileChooser.setCurrentDirectory(JFileChooser.java:563)
        at javax.swing.plaf.metal.MetalFileChooserUI$DirectoryComboBoxAction.actionPerformed(MetalFileChooserUI.java:1137)
        at javax.swing.JComboBox.fireActionEvent(JComboBox.java:1207)
        at javax.swing.JComboBox.contentsChanged(JComboBox.java:1278)
        at javax.swing.AbstractListModel.fireContentsChanged(AbstractListModel.java:100)
        at javax.swing.plaf.metal.MetalFileChooserUI$DirectoryComboBoxModel.setSelectedItem(MetalFileChooserUI.java:1000)
        at javax.swing.plaf.metal.MetalFileChooserUI$DirectoryComboBoxModel.addItem(MetalFileChooserUI.java:971)
Posted Date : 2005-11-24 20:07:58.0
Comments
  
  Include a link with my name & email   


PLEASE NOTE: JDK6 is formerly known as Project Mustang