search improved
This commit is contained in:
parent
24a0ab7451
commit
0867fa0dab
@ -77,10 +77,8 @@ public class FileOperations {
|
||||
while (true) {
|
||||
try {
|
||||
Path sourcePath = source.toPath();
|
||||
System.out.println("[DEBUG] Processing item: " + sourcePath + " (isLink: " + Files.isSymbolicLink(sourcePath) + ")");
|
||||
if (Files.isSymbolicLink(sourcePath)) {
|
||||
SymlinkResponse sRes = getSymlinkResponse(source, callback, globalSymlinkResponse);
|
||||
System.out.println("[DEBUG] Symlink response for " + source.getName() + ": " + sRes);
|
||||
if (sRes == SymlinkResponse.CANCEL) {
|
||||
return;
|
||||
}
|
||||
@ -91,33 +89,27 @@ public class FileOperations {
|
||||
// If FOLLOW, sRes will be FOLLOW or FOLLOW_ALL
|
||||
if (Files.exists(sourcePath)) {
|
||||
if (Files.isDirectory(sourcePath)) {
|
||||
System.out.println("[DEBUG] Following symlink as directory: " + sourcePath);
|
||||
copyDirectory(sourcePath, target.toPath(), totalItems, currentItem, callback, globalResponse, globalSymlinkResponse, null);
|
||||
// Count the symlink itself as processed to match countItems prediction
|
||||
currentItem[0]++;
|
||||
} else {
|
||||
System.out.println("[DEBUG] Following symlink as file: " + sourcePath);
|
||||
copyFileWithProgress(sourcePath, target.toPath(), totalItems, currentItem, callback);
|
||||
}
|
||||
} else {
|
||||
// Link is broken, follow is impossible, copy as link
|
||||
System.out.println("[DEBUG] Broken link encountered, copying as link: " + sourcePath);
|
||||
copySymlink(sourcePath, target.toPath(), totalItems, currentItem, callback);
|
||||
}
|
||||
break;
|
||||
} else if (source.isDirectory()) {
|
||||
System.out.println("[DEBUG] Copying directory: " + sourcePath);
|
||||
copyDirectory(sourcePath, target.toPath(), totalItems, currentItem, callback, globalResponse, globalSymlinkResponse, null);
|
||||
// Directory itself counted in countItems but copyDirectory skips increment for root.
|
||||
// So we increment here.
|
||||
currentItem[0]++;
|
||||
} else {
|
||||
System.out.println("[DEBUG] Copying file: " + sourcePath);
|
||||
copyFileWithProgress(sourcePath, target.toPath(), totalItems, currentItem, callback);
|
||||
}
|
||||
break;
|
||||
} catch (IOException e) {
|
||||
System.out.println("[DEBUG] Error processing " + source.getName() + ": " + e.getMessage());
|
||||
if (callback != null) {
|
||||
ErrorResponse res = callback.onError(source, e);
|
||||
if (res == ErrorResponse.ABORT) throw e;
|
||||
@ -253,7 +245,6 @@ public class FileOperations {
|
||||
public FileVisitResult visitFile(Path file, BasicFileAttributes attrs) throws IOException {
|
||||
if (callback != null && callback.isCancelled()) return FileVisitResult.TERMINATE;
|
||||
Path targetFile = finalTarget.resolve(effectiveSource.relativize(file.toAbsolutePath().normalize()));
|
||||
System.out.println("[DEBUG] Recursive visit (file): " + file + " (isLink: " + attrs.isSymbolicLink() + ")");
|
||||
|
||||
if (Files.exists(targetFile, LinkOption.NOFOLLOW_LINKS)) {
|
||||
if (globalResponse[0] == OverwriteResponse.NO_TO_ALL) return FileVisitResult.CONTINUE;
|
||||
@ -648,8 +639,28 @@ public class FileOperations {
|
||||
}
|
||||
|
||||
private static void searchRecursive(Path directory, String patternLower, Pattern filenameRegex, Pattern contentPattern, boolean recursive, boolean searchArchives, SearchCallback callback) throws IOException {
|
||||
if (callback != null && callback.isCancelled()) return;
|
||||
|
||||
// Use absolute path for reliable prefix checking
|
||||
Path absolutePath = directory.toAbsolutePath().normalize();
|
||||
String pathStr = absolutePath.toString();
|
||||
|
||||
callback.onProgress(pathStr);
|
||||
|
||||
// Skip troublesome virtual filesystems and device nodes
|
||||
if (pathStr.startsWith("/proc") || pathStr.startsWith("/sys") || pathStr.startsWith("/dev")) {
|
||||
return;
|
||||
}
|
||||
|
||||
try (DirectoryStream<Path> stream = Files.newDirectoryStream(directory)) {
|
||||
for (Path entry : stream) {
|
||||
if (callback != null && callback.isCancelled()) return;
|
||||
try {
|
||||
// Always skip symbolic links during search to prevent infinite loops and hangs on special files
|
||||
if (Files.isSymbolicLink(entry)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (Files.isDirectory(entry)) {
|
||||
if (recursive) {
|
||||
searchRecursive(entry, patternLower, filenameRegex, contentPattern, recursive, searchArchives, callback);
|
||||
@ -675,9 +686,12 @@ public class FileOperations {
|
||||
searchInArchiveCombined(file, patternLower, filenameRegex, contentPattern, callback);
|
||||
}
|
||||
}
|
||||
} catch (AccessDeniedException e) {
|
||||
// Silently skip
|
||||
}
|
||||
}
|
||||
} catch (AccessDeniedException e) {
|
||||
// Ignore directories without access
|
||||
// Silently skip
|
||||
}
|
||||
}
|
||||
|
||||
@ -699,7 +713,7 @@ public class FileOperations {
|
||||
}
|
||||
}
|
||||
} catch (IOException ex) {
|
||||
// Skip files that cannot be read as text
|
||||
// Silently skip
|
||||
}
|
||||
return false;
|
||||
}
|
||||
@ -783,12 +797,14 @@ public class FileOperations {
|
||||
}
|
||||
|
||||
private static void searchInArchiveCombined(File archive, String patternLower, Pattern filenameRegex, Pattern contentPattern, SearchCallback callback) {
|
||||
if (callback != null && callback.isCancelled()) return;
|
||||
String name = archive.getName().toLowerCase();
|
||||
try {
|
||||
if (name.endsWith(".zip") || name.endsWith(".jar")) {
|
||||
try (ZipInputStream zis = new ZipInputStream(new FileInputStream(archive))) {
|
||||
ZipEntry entry;
|
||||
while ((entry = zis.getNextEntry()) != null) {
|
||||
if (callback.isCancelled()) return;
|
||||
if (!entry.isDirectory()) {
|
||||
boolean nameMatched = true;
|
||||
if (patternLower != null && !patternLower.isEmpty()) {
|
||||
@ -812,6 +828,7 @@ public class FileOperations {
|
||||
try (org.apache.commons.compress.archivers.tar.TarArchiveInputStream tais = new org.apache.commons.compress.archivers.tar.TarArchiveInputStream(is)) {
|
||||
org.apache.commons.compress.archivers.tar.TarArchiveEntry entry;
|
||||
while ((entry = tais.getNextTarEntry()) != null) {
|
||||
if (callback.isCancelled()) return;
|
||||
if (!entry.isDirectory()) {
|
||||
boolean nameMatched = true;
|
||||
if (patternLower != null && !patternLower.isEmpty()) {
|
||||
@ -831,6 +848,7 @@ public class FileOperations {
|
||||
try (org.apache.commons.compress.archivers.sevenz.SevenZFile sevenZFile = new org.apache.commons.compress.archivers.sevenz.SevenZFile(archive)) {
|
||||
org.apache.commons.compress.archivers.sevenz.SevenZArchiveEntry entry;
|
||||
while ((entry = sevenZFile.getNextEntry()) != null) {
|
||||
if (callback.isCancelled()) return;
|
||||
if (!entry.isDirectory()) {
|
||||
boolean nameMatched = true;
|
||||
if (patternLower != null && !patternLower.isEmpty()) {
|
||||
@ -858,6 +876,7 @@ public class FileOperations {
|
||||
} else if (name.endsWith(".rar")) {
|
||||
try (com.github.junrar.Archive rar = new com.github.junrar.Archive(archive)) {
|
||||
for (com.github.junrar.rarfile.FileHeader fh : rar.getFileHeaders()) {
|
||||
if (callback.isCancelled()) return;
|
||||
if (!fh.isDirectory()) {
|
||||
boolean nameMatched = true;
|
||||
if (patternLower != null && !patternLower.isEmpty()) {
|
||||
@ -876,7 +895,9 @@ public class FileOperations {
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (Exception ignore) {}
|
||||
} catch (Exception e) {
|
||||
// Silently skip archive errors during search
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -1063,5 +1084,7 @@ public class FileOperations {
|
||||
*/
|
||||
public interface SearchCallback {
|
||||
void onFileFound(File file, String virtualPath);
|
||||
default boolean isCancelled() { return false; }
|
||||
default void onProgress(String status) {}
|
||||
}
|
||||
}
|
||||
|
||||
@ -26,6 +26,7 @@ public class SearchDialog extends JDialog {
|
||||
private JTable resultsTable;
|
||||
private ResultsTableModel tableModel;
|
||||
private JButton searchButton;
|
||||
private JButton stopButton;
|
||||
private JButton cancelButton;
|
||||
private JButton viewButton;
|
||||
private JButton editButton;
|
||||
@ -263,6 +264,14 @@ public class SearchDialog extends JDialog {
|
||||
searchButton = new JButton("Search");
|
||||
searchButton.addActionListener(e -> performSearch());
|
||||
|
||||
stopButton = new JButton("Cancel");
|
||||
stopButton.setEnabled(false);
|
||||
stopButton.addActionListener(e -> {
|
||||
searching = false;
|
||||
stopButton.setEnabled(false);
|
||||
statusLabel.setText("Cancelling...");
|
||||
});
|
||||
|
||||
viewButton = new JButton("View");
|
||||
viewButton.setEnabled(false);
|
||||
viewButton.addActionListener(e -> viewSelectedFile());
|
||||
@ -272,12 +281,16 @@ public class SearchDialog extends JDialog {
|
||||
editButton.addActionListener(e -> editSelectedFile());
|
||||
|
||||
cancelButton = new JButton("Close");
|
||||
cancelButton.addActionListener(e -> dispose());
|
||||
cancelButton.addActionListener(e -> {
|
||||
searching = false;
|
||||
dispose();
|
||||
});
|
||||
|
||||
JButton openButton = new JButton("Open location");
|
||||
openButton.addActionListener(e -> openSelectedFile());
|
||||
|
||||
buttonPanel.add(searchButton);
|
||||
buttonPanel.add(stopButton);
|
||||
buttonPanel.add(viewButton);
|
||||
buttonPanel.add(editButton);
|
||||
buttonPanel.add(openButton);
|
||||
@ -494,6 +507,7 @@ public class SearchDialog extends JDialog {
|
||||
|
||||
tableModel.clear();
|
||||
searchButton.setEnabled(false);
|
||||
stopButton.setEnabled(true);
|
||||
searching = true;
|
||||
|
||||
// Persist the chosen patterns into history
|
||||
@ -544,11 +558,25 @@ public class SearchDialog extends JDialog {
|
||||
|
||||
// Spustit vyhledávání v samostatném vlákně
|
||||
SwingWorker<Void, Object[]> worker = new SwingWorker<Void, Object[]>() {
|
||||
private String currentSearchPath = "";
|
||||
|
||||
@Override
|
||||
protected Void doInBackground() throws Exception {
|
||||
FileOperations.search(searchDirectory, finalNamePat, finalContentPat, recursiveCheckBox.isSelected(), searchArchives, (file, virtualPath) -> {
|
||||
if (!searching) return;
|
||||
publish(new Object[]{file, virtualPath});
|
||||
FileOperations.search(searchDirectory, finalNamePat, finalContentPat, recursiveCheckBox.isSelected(), searchArchives, new FileOperations.SearchCallback() {
|
||||
@Override
|
||||
public void onFileFound(File file, String virtualPath) {
|
||||
publish(new Object[]{"file", file, virtualPath});
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isCancelled() {
|
||||
return !searching;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onProgress(String status) {
|
||||
publish(new Object[]{"progress", status});
|
||||
}
|
||||
});
|
||||
return null;
|
||||
}
|
||||
@ -556,25 +584,41 @@ public class SearchDialog extends JDialog {
|
||||
@Override
|
||||
protected void process(List<Object[]> chunks) {
|
||||
for (Object[] chunk : chunks) {
|
||||
File file = (File) chunk[0];
|
||||
String virtualPath = (String) chunk[1];
|
||||
String type = (String) chunk[0];
|
||||
if ("file".equals(type)) {
|
||||
File file = (File) chunk[1];
|
||||
String virtualPath = (String) chunk[2];
|
||||
tableModel.addResult(new FileItem(file, virtualPath));
|
||||
// update found count and status
|
||||
foundCount++;
|
||||
statusLabel.setText("Found " + foundCount + " — searching...");
|
||||
// If this is the first found file, select it and focus the table
|
||||
updateStatusLabel(currentSearchPath);
|
||||
if (foundCount == 1) {
|
||||
try {
|
||||
resultsTable.setRowSelectionInterval(0, 0);
|
||||
resultsTable.requestFocusInWindow();
|
||||
} catch (Exception ignore) {}
|
||||
}
|
||||
} else if ("progress".equals(type)) {
|
||||
currentSearchPath = (String) chunk[1];
|
||||
updateStatusLabel(currentSearchPath);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void updateStatusLabel(String currentPath) {
|
||||
String text = "Found " + foundCount;
|
||||
if (currentPath != null && !currentPath.isEmpty()) {
|
||||
text += " — " + currentPath;
|
||||
} else {
|
||||
text += " — searching...";
|
||||
}
|
||||
statusLabel.setText(text);
|
||||
statusLabel.setToolTipText(currentPath);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void done() {
|
||||
searchButton.setEnabled(true);
|
||||
stopButton.setEnabled(false);
|
||||
searching = false;
|
||||
// finalize status
|
||||
statusProgressBar.setIndeterminate(false);
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user