It seems like the long-running tree walker task should be defined in a class like so:
public class TreeWalker extends SwingWorker<Void,String> implements FileVisitor<Path>
And begun somewhere like so:
TreeWalker walker = (new TreeWalker());
walker.execute();
The long-running task is not only initiated by but completely carried out by a single call to walkFileTree()
, a method in the Files
class. So surely the call to it has to be in doInBackGround()
.
protected Void doInBackground() throws Exception {
Files.walkFileTree(SearchyGUI.p , this);
return null;
}
Note that walkTreeFile()
internally calls four methods for each file encountered. A programmer-written loop isn't feasible. So there's my problem. How do I use publish()
to send file information as a string to the process
method I need to override? Examples I've seen have publish()
inside doInBackground()
, but inside a loop, which is not possible here.
The one of the four methods that I care most about is visitFile()
, which walkFileTree()
needs to be able to find and I suspect this is where to put publish()
:
public FileVisitResult visitFile(Path f, BasicFileAttributes a) throws IOException {
if (...we want this file...)
publish(f.toString());
return CONTINUE;
}
I could put all 4 methods that walkFileTree() calls in an inner class inside doInBackground()
, but that seems like wishful thinking.
P.S. I can't use get()
; that's the whole point (as I understand it)--too much delay in getting results (may process thousands of files to find a dozen) to wait until doInBackground() ends.
==========================================
EDIT #3, 50 minutes after original post time
public static void doIt(){
try {
System.out.println("It begins..."); // This does happen.
TreeWalker walker = new TreeWalker();
walker.execute();
SearchyGUI.info.setVisible(true); // Form is displayed, stays blank.
}
catch (Exception e) { System.out.println("Uh-oh"); } // This does NOT happen.
}
==========================================
(EDIT #2, 40 minutes after post)
Here's my process method. The println didn't execute.
protected void process(String s) {
System.out.println("in process()...");
report(s); // my method to append text area with another line of file info
}
Also, the class statement that contains doInBackground()
has changed:
public class TreeWalker extends SwingWorker<Void, String> implements Runnable{
The Walking
class is nested within doInBackground()
.
==========================================
(EDIT, 20 minutes after post)
This compiled but did nothing:
protected Void doInBackground() throws Exception
{
class Walking implements FileVisitor<Path>
{
@Override
public FileVisitResult visitFile(Path f, BasicFileAttributes a) throws IOException
{
String modifyDate = a.lastModifiedTime().toString().substring(0,10);
String fpathname = f.toString();// + "\\" + f.getFileName().toString());
if (...we want this one...)
publish(f.getFileName());
return disposition;
}
... other methods excluded
} // end inner class
System.out.println("walking??"); // We get here ...
Files.walkFileTree(SearchyGUI.p , (FileVisitor<? super Path>) this);
System.out.println("Finished walking??"); // ... but not here.
return null;
} // end of doInBackground()
=============================
... another freakin' edit ... my current class defs...
public class GUI extends JFrame implements ActionListener, MouseListener, KeyListener
public class TreeWalker extends SwingWorker<Void, String> implements Runnable{
protected Void doInBackground() throws Exception {
class Walking implements FileVisitor<Path>{ // CLASS INSIDE doInBackground
... zzzzzzzzzzzzzzzzzzz ...........
Because your TreeWalker
extends both SwingWorker
and implements FileVisitor
, you could be able to call publish
from within any of the call back methods, for example...
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
publish(dir.toString());
return FileVisitResult.CONTINUE;
}
Now, based on your needs, you would need to convert the Path
element to a String
using what ever method you need...
Updated with working example
import java.io.IOException;
import java.nio.file.FileVisitResult;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.List;
import java.util.concurrent.ExecutionException;
import javax.swing.SwingWorker;
public class TreeWalkerExample {
public static void main(String[] args) {
new TreeWalkerExample();
}
public TreeWalkerExample() {
TreeWalker tw = new TreeWalker();
tw.execute();
try {
tw.get();
} catch (InterruptedException | ExecutionException ex) {
ex.printStackTrace();
}
}
public class TreeWalker extends SwingWorker<Void, Path> implements FileVisitor<Path> {
@Override
protected void process(List<Path> chunks) {
for (Path p : chunks) {
System.out.println(p);
}
}
@Override
protected Void doInBackground() throws Exception {
Path p = Paths.get(System.getProperty("user.home"));
System.out.println(p);
Files.walkFileTree(p, this);
return null;
}
@Override
public FileVisitResult preVisitDirectory(Path dir, BasicFileAttributes attrs) throws IOException {
FileVisitResult fvr = FileVisitResult.CONTINUE;
if (dir.getFileName().toString().startsWith(".")) {
fvr = FileVisitResult.SKIP_SUBTREE;
}
return fvr;
}
@Override
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
publish(file);
return FileVisitResult.CONTINUE;
}
@Override
public FileVisitResult visitFileFailed(Path file, IOException exc) throws IOException {
return FileVisitResult.TERMINATE;
}
@Override
public FileVisitResult postVisitDirectory(Path dir, IOException exc) throws IOException {
return FileVisitResult.CONTINUE;
}
}
}
Nb, this doesn't have a GUI with it, but instead waits for the worker to complete by waiting for get
to return is only meant as an example
Since @Madprogrammer didn't use a GUI and DID use get() [which WAITS until execution of doInBackground() finishes], I added a GUI, modified his publish(), and included a call to done(), just as icing on the cake. My own tree walker doesn't work yet, but Mad's shown me the way. Here are the highlights of the new Mad-with-GUI version.
public class TreeWalkerExample {
static GUI gui;
public static void main(String args[])
{...invokelater...
public void run() {
gui = new GUI();
gui.setVisible(true); }
}
public TreeWalkerExample() {
(new TreeWalker()).execute();
}
public class TreeWalker extends SwingWorker<Void,Path> implements FileVisitor<Path> {
protected Void doInBackground() throws Exception {
Path p = Paths.get("C:\\","Users","\\Dave","\\Documents","\\Java");
gui.appendOutput(p.toString());
Files.walkFileTree(p, this);
return null;
}
public FileVisitResult visitFile(Path file, BasicFileAttributes a) throws IOException{
publish(file);
return FileVisitResult.CONTINUE;
}
protected void process(List<Path> chunks) {
for (Path p : chunks)
gui.appendOutput(p.getFileName().toString());
}
protected void done(){
gui.appendOutput("\nDone");
}
}
===================================================================================================
public class GUI extends javax.swing.JFrame {
JTextArea output;
private void btnWalkMouseClicked(java.awt.event.MouseEvent evt) {
new TreeWalkerExample();
}
public void appendOutput(String s){
output.append("\n" + s);
}
It's not that I gave up on SwingWorker, I just determined that I didn't know squat about threads and decided to do something about it. My success the past 2 days with a much simpler project led me to apply that same strategy to my (various) Treewalker (projects), which now: (1) do not make the screen flash when appending output to a textarea and (2) end gracefully and immediately with a buttonpress.
All it took was using a separate thread (not a SwingWorker) for the "background" FileVisitor
task, which:
(a) let GUI stay "in charge" and thus be able to accept output seamlessly as well as provide a button for the user to press to abort and
(b) makes the code look sane and easy to follow.
So @Mad, thanks AGAIN for the help. (I haven't been working solely on this since Nov. 19! I got so frustrated I just left it, did other stuff successfully, and got the nerve to come back and try again).
P.S. I found the text Ivar Horton's Beginning Java (7) invaluable. Best I've seen about threads.
FWIW here's an outline of my backup program:
public class Copy extends Thread{
public static FileVisitResult disposition = FileVisitResult.CONTINUE;
static Thread t ;
static FilesCopied output ;
...
public static TreeWalker fv;
...
public void run() {
...
fv = new TreeWalker();
try {
Files.walkFileTree(UserIO.inputPath,fv);
}
catch ...
}
public /* inner */ class TreeWalker implements FileVisitor<Path> {
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
maybeCopy(file);
return disposition;
}
public FileVisitResult preVisitDirectory(Path d, BasicFileAttributes a) throws IOException {
maybeMakeDir(d,fromRootDepth);
return disposition;
}
...
} // end TreeWalker
...
public static void main(String[] args) throws IOException {
EventQueue.invokeLater(new Runnable()
public void run() { gui = new UserIO(); gui.setVisible(true);
}});
EventQueue.invokeLater(new Runnable() {
public void run() {
output = new FilesCopied();
}});
t = new Copy();
}
} // end class Copy
======================
public class UserIO extends JFrame {
...
public void btnBackupMouseClicked(MouseEvent evt) throws IOException {
...
Copy.t.start();
}
public void btnStopMouseClicked(MouseEvent evt) throws IOException {
Copy.disposition = FileVisitResult.TERMINATE;
}
}
来源:https://stackoverflow.com/questions/20060366/swingworker-with-filevisitor-class-and-walkfiletree-which-iterates-internally