|
Quick Lists
|
|
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
|
PLEASE NOTE: JDK6 is formerly known as Project Mustang
|
|
|
 |