JChooser : Restricting view and add to a particular directory tree - java

This is a question that has been asked before, but there is a bug in that solution.
My requirements are - when user invokes JFileChooser, he should not be able to navigate up the tree.
The answer has been to write a custom FileSystemView -
How do I restrict JFileChooser to a directory?
Has a solution, as does
http://tips4java.wordpress.com/2009/01/28/single-root-file-chooser/
However, both of them have a problem sometimes. For the correct implementation, the FileChooser should come up as -
However, sometimes, it appears as -
and user has to click the up arrow to see the correct directory. Any idea why this is happening please? And a solution ?
The code for the custom FileSystemView is
public class SingleRootFileSystemView extends FileSystemView
{
File root;
File[] roots = new File[1];
public SingleRootFileSystemView(File root)
{
super();
this.root = root;
roots[0] = root;
}
#Override
public File createNewFolder(File containingDir)
{
File folder = new File(containingDir, "New Folder");
folder.mkdir();
return folder;
}
#Override
public File getDefaultDirectory()
{
return root;
}
#Override
public File getHomeDirectory()
{
return root;
}
#Override
public File[] getRoots()
{
return roots;
}
}
The code to invoke this is
FileSystemView fsv = new SingleRootFileSystemView(folder);
JFileChooser fileChooser = new JFileChooser(fsv.getHomeDirectory(),fsv);
fileChooser.setFileSelectionMode(1);
int returnVal = fileChooser.showOpenDialog(null);
Thanks very much.

Related

Calculating number of Messages in Folders in a parent-child relationship recursively using Java?

Each Folder has list of Messages and each Folder can have list of its children Folders and list of its own Messages. I'm struggling with recursion function which needs to return count of Message objects from any node Folder that is passed to that functions. Here are classes and some dummy date inserted to form a tree.
If I'm correct, this is not like Java's files and folders system. Here, Message cannot be Folder, so there is no Java's isDirectory()
public class Folder {
private Folder parent;
private ArrayList<Message> mMessages;
private ArrayList<Folder> children;
public Folder(){
this(null, new ArrayList<Folder>(), new ArrayList<Message>());
}
public Folder(Folder parent, ArrayList<Folder> children) {
this.parent = parent;
this.children = children;
}
public Folder(Folder parent, ArrayList<Folder> childrens, ArrayList<Message> messages ) {
this.parent = parent;
this.children = childrens;
this.mMessages = messages;
}
public boolean hasChildren(){
return this.getChildren().size() > 0;
}
public void addChildren(Folder child) {
child.setParent(this);
this.getChildren().add(child);
}
public void addMessage(Message message) {
this.getMessages().add(message);
}
public boolean isRoot() {
return parent == null;
}
}
Here are dummy data:
Message m1 = new Message();
Message m2 = new Message();
Message m3 = new Message();
//main root
Folder root = new Folder();
//root's folder children
Folder level1a = new Folder(); level1a.addMessage(m1);
Folder level1b = new Folder(); level1b.addMessage(m2);
Folder level1c = new Folder(); level1c.addMessage(m3);
//adding children to root
root.addChildren(level1a); root.addChildren(level1b); root.addChildren(level1c);
//adding children to level 1a
Folder level1aa = new Folder();
level1a.addChildren(level1aa);
//adding messages to level 1aaa and 1aab
Folder level1aaa = new Folder(); level1aaa.addMessage(new Message());
Folder level1aab = new Folder(); level1aab.addMessage(new Message());
//adding children (level1aaa and level1aab) to its parent level1aa
level1aa.addChildren(level1aaa);
level1aa.addChildren(level1aab);
This what I have. It is not correct, with each new level adedd, if root is passed to the function, I get one extra count more. But if I pass some node Folder from any level between leaf and root, I get correct result. I also tried with the debugger, slowly, writing stack frames like from lectures. But can't seem to wrap my mind around this.
public static int numberOfMessages(Folder folder){
int count = 0;
count+= folder.getMessages().size();
for (Folder child : folder.getChildren()) {
if (child.hasChildren()) {
count += numberOfMessages(child);
}
count += child.getMessages().size();
}
return count;
}
From what I figured, it seems when it starts returning calls somehow level1a's Message get counted two times. I don't know why. I would appreciate any help.
Actually, just now, I came up with this. If someone can verify or try to suggest me how to improve it.
public static int numberOfMessages(Folder folder){
int count = 0;
for (Folder child : folder.getChildren()) {
count += numberOfMessages(child);
}
return count + folder.getMessages().size();
}

javafx: How do I create a TreeItem that displays a tooltip that contains the file path and load the file path on double click?

I am trying to attach a tooltip, which contains the file path, to the TreeItem<String>, such that when I hover around this TreeItem, it will display a text of the file path when I hover my mouse around it. This does not work in my code, as it complains I cannot install this on to a String. How can I solve this problem?
Second, I want to be able to double click on the TreeItem then it can automatically load the file. How can I achieve that ?
#FXML
TreeView<String> fxFileTree;
public void defineFileTree(){
TreeItem<String> root = new TreeItem<String>("Portfolio");
fxFileTree.setShowRoot(true);
root.setExpanded(true);
fxFileTree.setRoot(root);
}
public void populateTree(String fileName, String filePath){
addLeaf(fileName, (TreeItem<String>) fxFileTree.getRoot(), filePath);
}
public void addLeaf(String leaf, TreeItem<String> parent, String filePath{
TreeItem<String> item = new TreeItem<>(leaf);
Tooltip.install(item,filepath) // <- This is wrong
parent.getChildren().add(item);
}
UPDATE: the goal of this exercise is to build a tree which only contains the root and one level of branches, i.e. root -> leaf1 (stop here,no grandchildren for root, children only). The root will just be a title String. And I want to add leaves to the root. The leaf is a file object. The display text of the leaf will be the file name and install the tooltip for this leaf. The tooltip will show the file path.
You can't set a tooltip on a TreeItem. TreeItems represent the data displayed in a tree, they are not UI components. You need to set the tooltip on the TreeCells, which you can do in a factory.
Since you are going to need access to the data about the file, you should not be using TreeView<String> and TreeItem<String>: you should either use TreeView<File> or TreeView<Path> (in other words, make the data type of the tree either File or Path). So you would do something like:
#FXML
private TreeView<Path> fxFileTree ;
private TreeItem<Path> root ;
// ...
public void initialize() {
fxFileTree.setCellFactory(tv -> {
final Tooltip tooltip = new Tooltip();
TreeCell<Path> cell = new TreeCell<Path>() {
#Override
public void updateItem(Path item, boolean empty) {
super.updateItem(item, empty);
if (empty) {
setText(null);
setTooltip(null);
} else if (getTreeItem() == root) {
setText("Portfolio");
setTooltip(null);
} else {
setText(item.getFileName().toString());
tooltip.setText(item.toRealPath().toString());
setTooltip(tooltip);
}
}
};
cell.setOnMouseClicked(e -> {
if (e.getClickCount() == 2 && ! cell.isEmpty()) {
Path file = cell.getItem();
// do whatever you need with path...
}
});
return cell ;
});
}
public void defineFileTree(){
root = new TreeItem<>(null);
fxFileTree.setShowRoot(true);
root.setExpanded(true);
fxFileTree.setRoot(root);
}
// ...

Recursively list subdirectories/files while applying a filter

I have following directory hierarchy
$ ls -R a
b c
a/b:
x.db y.db
a/b/x.db:
p0.txt p1.txt
a/b/y.db:
a/c:
m.db
a/c/m.db:
I need foo(FIle rootDir){
....
}
which outputs ["a/b/x.db", "a/b/y.db", "a/c/m.db"]
for foo(new File("a"))
I have tried using org.apache.commons.io.FileUtils unsuccessfully!
Following is what I tried
private static Collection<File> foo(File rootDir) {
return FileUtils.listFilesAndDirs(rootDir,
FalseFileFilter.INSTANCE,
new DirectoryFileFilter() {
#Override
public boolean accept(File file) {
if (file.getName().endsWith("db")) {
return true;
}
return false;
}
}
);
}
This outputs
[<parent_dir>/a]
Can someone help me out? Thanks in advance!!
public void findFile(File dir){
for(File f: dir.listFiles()){
if(f.isDirectory()){
findFile(f);
}else if(f.getName().endsWith(".db")){
System.out.prinltn(f.getName());
}
}
}
See the Java tutorial Walking the File Tree.
In particular look at Finding Files.
The example code find.java "recurses a file tree looking for files and directories that match a particular glob (filter) pattern."

Problems updating JTree changes with JButton

I'm on a project with several people, and my task is to compile a working JTree to show the structure of a set directory. As it is, the JTree shows the correct structure of files and folders when the class starts. However, I can't seem to get it to be able to update when files are added or subtracted (not with the JTree). In my happy ignorance (quite presistant that one) I added a JButton with;
treeModel.reload();
...and hoped that would do the trick. It reloaded the JTree sure enough, but didn't change the file structure, even though several files were added after the class loaded.
And it is thus I place my trust in this community to both point out the source of my issues, and other lackings in semantics and logic. I'm a willing learner.
public class FileTree extends JPanel {
private JTree tree;
private DefaultMutableTreeNode root;
private DefaultTreeModel treeModel;
public FileTree(JPanel jp) {
jp.setLayout(new BorderLayout());
final File directory = getDir();
createTree(directory);
tree = new JTree(treeModel);
tree.setRootVisible(true);
tree.setShowsRootHandles(true);
tree.getSelectionModel().setSelectionMode(TreeSelectionModel.SINGLE_TREE_SELECTION);
tree.setCellRenderer(new FileTreeCellRenderer());
tree.addMouseListener(new MouseAdapter() {
public void mousePressed(MouseEvent e) {
TreePath selPath = tree.getPathForLocation(e.getX(), e.getY());
if(e.getClickCount() == 2) {
Object n = selPath.getLastPathComponent();
String sel = n.toString();
File nodeFile = new File(sel);
if(nodeFile.isDirectory() && (!nodeFile.isHidden())) {
UserWindow.printToLog("doubble-click event on folder: " + nodeFile.getName());
//TODO:stuff happening here
}
if(nodeFile.isFile() && (!nodeFile.isHidden())) {
UserWindow.printToLog("doubble-click event on file: " + nodeFile.getName());
new FileTreeEventHandler(nodeFile);
}
}
}
});
JScrollPane sp = new JScrollPane(tree);
jp.add(sp);
JButton updateButton = new JButton("Update");
updateButton.addActionListener(new ActionListener() {
#Override
public void actionPerformed(ActionEvent arg0) {
treeModel.reload();
}
});
jp.add(updateButton, BorderLayout.SOUTH);
setVisible(true);
}
private void createTree(File directory) {
root = new DefaultMutableTreeNode();
treeModel = new DefaultTreeModel(root);
DefaultMutableTreeNode node = new DefaultMutableTreeNode(directory);
root.add(node);
populate(directory, node);
}
private void populate(File directory, DefaultMutableTreeNode node) {
String[] files = directory.list();
for(String file : files) {
File currentFile = new File(directory, file);
addLeaf(node, currentFile);
}
}
private void addLeaf(DefaultMutableTreeNode node, File currentFile) {
if(currentFile.isFile() && !currentFile.isHidden()) {
DefaultMutableTreeNode leafFile = new DefaultMutableTreeNode(currentFile);
node.add(leafFile);
}
if(currentFile.isDirectory() && !currentFile.isHidden()) {
DefaultMutableTreeNode folder = new DefaultMutableTreeNode(currentFile);
node.add(folder);
populate(currentFile, folder);
}
}
private File getDir() {
String path = new File(System.getProperty("user.dir"), "downloaded content").getAbsolutePath();
File dir = new File(path);
if(!dir.exists()) {
dir.mkdirs();
}
return dir;
}
}
treeModel.reload();
Instead of just invoking the above code, I would guess you need to invoke:
createTree(directory);
tree.setModel(treeModel);
to recreate the TreeModel to reflect the new directory structure.
DefaultTreeModel#reload basically states...
Invoke this method if you've modified the TreeNodes upon which this
model depends. The model will notify all of its listeners that the
model has changed below the given node.
This assumes that the model itself has changed.
You have at least two basic course of actions...
You could...
Create a new DefaultTreeModel or
Remove all the nodes from the existing DefaultTreeModel...
And then re-build the model.
If you create a new TreeModel, make sure you set it against the JTree.
This is a little heavy handed, but even if you choose to write the population algorithm so that it checked the existing content of the model, you're still going to have to walk the file structure.
Updating the model could allow you to preserve some of the state of the JTree (ie what nodes are expanded or not, for example)
If you're lucky enough to be using Java 7, you could also take advantage of the File Watcher API, which would allow you to, for example, set each directory node up as a watcher service to monitor it's own content and update itself (add/remove nodes) when changes occur.
Take a look at Watching a Directory for Changes

Using File.listFiles with FileNameExtensionFilter

I would like to get a list of files with a specific extension in a directory. In the API (Java 6), I see a method File.listFiles(FileFilter) which would do this.
Since I need a specific extension, I created a FileNameExtensionFilter. However I get a compilation error when I use listFiles with this. I assumed that since FileNameExtensionFilter implements FileFilter, I should be able to do this. Code follows:
FileNameExtensionFilter filter = new FileNameExtensionFilter("text only","txt");
String dir = "/users/blah/dirname";
File f[] = (new File(dir)).listFiles(filter);
The last line shows a compilation error:
method listFiles(FileNameFilter) in type File is not applicable for arguments of type FileNameExtensionFilter
I am trying to use listFiles(FileFilter), not listFiles(FileNameFilter). Why does the compiler not recognize this?
This works if I write my own extension filter extending FileFilter. I would rather use FileNameExtensionFilter than write my own. What am I doing wrong?
The FileNameExtensionFilter class is intended for Swing to be used in a JFileChooser.
Try using a FilenameFilter instead. For example:
File dir = new File("/users/blah/dirname");
File[] files = dir.listFiles(new FilenameFilter() {
public boolean accept(File dir, String name) {
return name.toLowerCase().endsWith(".txt");
}
});
One-liner in java 8 syntax:
pdfTestDir.listFiles((dir, name) -> name.toLowerCase().endsWith(".txt"));
Is there a specific reason you want to use FileNameExtensionFilter? I know this works..
private File[] getNewTextFiles() {
return dir.listFiles(new FilenameFilter() {
#Override
public boolean accept(File dir, String name) {
return name.toLowerCase().endsWith(".txt");
}
});
}
With java lambdas (available since java 8) you can simply convert javax.swing.filechooser.FileFilter to java.io.FileFilter in one line.
javax.swing.filechooser.FileFilter swingFilter = new FileNameExtensionFilter("jpeg files", "jpeg");
java.io.FileFilter ioFilter = file -> swingFilter.accept(file);
new File("myDirectory").listFiles(ioFilter);
Here's something I quickly just made and it should perform far better than File.getName().endsWith(".xxxx");
import java.io.File;
import java.io.FileFilter;
public class ExtensionsFilter implements FileFilter
{
private char[][] extensions;
private ExtensionsFilter(String[] extensions)
{
int length = extensions.length;
this.extensions = new char[length][];
for (String s : extensions)
{
this.extensions[--length] = s.toCharArray();
}
}
#Override
public boolean accept(File file)
{
char[] path = file.getPath().toCharArray();
for (char[] extension : extensions)
{
if (extension.length > path.length)
{
continue;
}
int pStart = path.length - 1;
int eStart = extension.length - 1;
boolean success = true;
for (int i = 0; i <= eStart; i++)
{
if ((path[pStart - i] | 0x20) != (extension[eStart - i] | 0x20))
{
success = false;
break;
}
}
if (success)
return true;
}
return false;
}
}
Here's an example for various images formats.
private static final ExtensionsFilter IMAGE_FILTER =
new ExtensionsFilter(new String[] {".png", ".jpg", ".bmp"});
Duh.... listFiles requires java.io.FileFilter. FileNameExtensionFilter extends javax.swing.filechooser.FileFilter. I solved my problem by implementing an instance of java.io.FileFilter
Edit: I did use something similar to #cFreiner's answer. I was trying to use a Java API method instead of writing my own implementation which is why I was trying to use FileNameExtensionFilter. I have many FileChoosers in my application and have used FileNameExtensionFilters for that and I mistakenly assumed that it was also extending java.io.FileFilter.

Categories