Jfilechooser and Browsing Networked MAChines

How to navigate to a network host in JFileChooser?

I found a Windows-specific solution that allows navigating to any accessible computer node from its name alone (e.g. \\blah or \\blah\), without requiring enumeration of the Network shell folder, or any advance knowledge of network shares on the given node.

Creating a ShellFolder for a computer path

While debugging this issue I discovered that a ShellFolder has to be created for the given computer path to be able to navigate to it. Win32ShellFolderManager2.createShellFolder() will call File.getCanonicalPath() on the given file, which will in turn call WinNTFileSystem.canonicalize(). This last call always fails on computer paths. After much experimentation, I was able to create a ShellFolder for any accessible computer path by wrapping the File object in something that bypasses WinNTFileSystem.canonicalize():

/**
* Create a shell folder for a given network path.
*
* @param path File to test for existence.
* @return ShellFolder representing the given computer node.
* @throws IllegalArgumentException given path is not a computer node.
* @throws FileNotFoundException given path could not be found.
*/
public static ShellFolder getComputerNodeFolder(String path)
throws FileNotFoundException {
File file = new NonCanonicalizingFile(path);
if (ShellFolder.isComputerNode(file)) {
return new Win32ShellFolderManager2().createShellFolder(file);
} else {
throw new IllegalArgumentException("Given path is not a computer node.");
}
}

private static final class NonCanonicalizingFile extends File {
public NonCanonicalizingFile(String path) {
super(path);
}

@Override
public String getCanonicalPath() throws IOException {
// Win32ShellFolderManager2.createShellFolder() will call getCanonicalPath() on this file.
// Base implementation of getCanonicalPath() calls WinNTFileSystem.canonicalize() which fails on
// computer nodes (e.g. "\\blah"). We skip the canonicalize call, which is safe at this point because we've
// confirmed (in approveSelection()) that this file represents a computer node.
return getAbsolutePath();
}
}

Admittedly this solution has a couple edge-cases (e.g. \\blah\ works but \\blah\someShare\..\ does not), and ideally OpenJDK should fix these quirks on their end. This is also an OS-specific and implementation-specific solution, and will not work outside OpenJDK-on-Windows setup.

Integrating with JFileChooser: Option 1

The simplest way to integrate this with JFileChooser is to override its approveSelection() method. This allows user to type in a computer path (\\blah or \\blah\) in the dialog and press Enter to navigate there. An alert message is shown when a non-existent or non-accessible path was given.

JFileChooser chooser = new JFileChooser() {
@Override
public void approveSelection() {
File selectedFile = getSelectedFile();
if (selectedFile != null && ShellFolder.isComputerNode(selectedFile)) {
try {
// Resolve path and try to navigate to it
setCurrentDirectory(getComputerNodeFolder(selectedFile.getPath()));
} catch (FileNotFoundException ex) {
// Alert user if given computer node cannot be accessed
JOptionPane.showMessageDialog(this, "Cannot access " + selectedFile.getPath());
}
} else {
super.approveSelection();
}
}
};
chooser.showOpenDialog(null);

Integrating with JFileChooser: Option 2

Alternatively, FileSystemView can be augmented by overriding its createFileObject(String) method to check for computer paths. This allows passing a computer path to JFileChooser(String,FileSystemView) constructor and still allows user to navigate to accessible computer paths. However, there is still no easy way to message the user about non-accessible computer paths without overriding JFileChooser.approveSelection():

public static class ComputerNodeFriendlyFileSystemView extends FileSystemView {

private final FileSystemView delegate;

public ComputerNodeFriendlyFileSystemView(FileSystemView delegate) {
this.delegate = delegate;
}

@Override
public File createFileObject(String path) {
File placeholderFile = new File(path);
if (ShellFolder.isComputerNode(placeholderFile)) {
try {
return getComputerNodeFolder(path);
} catch (FileNotFoundException ex) {
return placeholderFile;
}
} else {
return delegate.createFileObject(path);
}
}

// All code below simply delegates everything to the "delegate"

@Override
public File createNewFolder(File containingDir) throws IOException {
return delegate.createNewFolder(containingDir);
}

@Override
public boolean isRoot(File f) {
return delegate.isRoot(f);
}

@Override
public Boolean isTraversable(File f) {
return delegate.isTraversable(f);
}

@Override
public String getSystemDisplayName(File f) {
return delegate.getSystemDisplayName(f);
}

@Override
public String getSystemTypeDescription(File f) {
return delegate.getSystemTypeDescription(f);
}

@Override
public Icon getSystemIcon(File f) {
return delegate.getSystemIcon(f);
}

@Override
public boolean isParent(File folder, File file) {
return delegate.isParent(folder, file);
}

@Override
public File getChild(File parent, String fileName) {
return delegate.getChild(parent, fileName);
}

@Override
public boolean isFileSystem(File f) {
return delegate.isFileSystem(f);
}

@Override
public boolean isHiddenFile(File f) {
return delegate.isHiddenFile(f);
}

@Override
public boolean isFileSystemRoot(File dir) {
return delegate.isFileSystemRoot(dir);
}

@Override
public boolean isDrive(File dir) {
return delegate.isDrive(dir);
}

@Override
public boolean isFloppyDrive(File dir) {
return delegate.isFloppyDrive(dir);
}

@Override
public boolean isComputerNode(File dir) {
return delegate.isComputerNode(dir);
}

@Override
public File[] getRoots() {
return delegate.getRoots();
}

@Override
public File getHomeDirectory() {
return delegate.getHomeDirectory();
}

@Override
public File getDefaultDirectory() {
return delegate.getDefaultDirectory();
}

@Override
public File createFileObject(File dir, String filename) {
return delegate.createFileObject(dir, filename);
}

@Override
public File[] getFiles(File dir, boolean useFileHiding) {
return delegate.getFiles(dir, useFileHiding);
}

@Override
public File getParentDirectory(File dir) {
return delegate.getParentDirectory(dir);
}
}

Usage:

ComputerNodeFriendlyFileSystemView fsv
= new ComputerNodeFriendlyFileSystemView(FileSystemView.getFileSystemView());
JFileChooser chooser = new JFileChooser("\\\\blah", fsv);
chooser.showOpenDialog(null);

JFilechooser for a remote server

JFileChooser can't load a URL. You will need to map a network share on the machine that wants to browse to the files.

connect to an IP address true a file sharing program

You're using "//" + hostname + "/C://" as the path for your JFileChooser. That's not a valid path. If you're trying to access files in a shared folder on a LAN, the path for that looks like \\hostname\sharename.

Even if no shared folders have been defined on the remote machine, may be an "administrative share" of the C: drive called C$, so you could use \\hostname\C$. But you have to authenticate as a valid user on that system to have permission to access the share. (I'm not sure how that'll work when you try to acccess the path from a Java program — Windows might pop up a login box for the remote system, or it might just fail.)

How do I restrict JFileChooser to a directory?

You can probably do this by setting your own FileSystemView.

How to restrict a JFileChooser to a custom file type?

I actually have a program I am writing at the moment that has multiple JFileChooser's, each of them requiring to look for only specific file types. This example would allow you to have the same idea, so that if in the future, you need to allow for different file types, you are ready to go. I have created a custom class that extends upon FileFilter

public class CustomExtension extends FileFilter
{
private String type;

public CustomExtension(String type)
{
this.type = type;
}

public Boolean accept(File file)
{
if(file.isDirectory())
return true;

String ext = getExtension(file);
if(ext == null)
return false;

switch(type)
{
case "battle":
if(ext.equals("battle"))
return true;
else
break;
default:
System.out.println(type + " has not been set up in the switch statement yet");
}

return false;
}

public String getDescription()
{
switch(type)
{
case "battle":
return "Only battle file supported";
}
}

public String getExtension(File f)
{
String ext = null;
String filename = f.getName();

int i = filename.lastIndexOf('.');

if(i > 0 && i < filename.length() - 1)
ext = s.substring(i + 1).toLowerCase();

return ext;
}
}

I have been using this for a while now and I haven't noticed any bugs. To set up a JFileChooser so that it uses this filter, you would use:

JFileChooser chooser = new JFileChooser();
chooser.setFileFilter(new CustomExtension("battle"));

Now your JFileChooser will only display directories, and files that end in .battle



Related Topics



Leave a reply



Submit