How to pass context menu parameters to Java ProcessBuilder - java

I am using Java ProcessBuilder to open a file with a specific program on windows.
That itself works fine, with the following code:
ProcessBuilder p = new ProcessBuilder();
p.command("C:\\Program Files (x86)\\...\\program.exe", file.getAbsolutePath());
What I want to do is to invoke the functionality of a file context menu entry from that program which looks like this:
"C:\Program Files (x86)\...\program.exe" /Enqueue "%1"
How do I have to pass those parameters to the process builder?
I already tried the following, none of which worked:
p.command("C:\\Program Files (x86)\\...\\program.exe","/Enqueue","%1",next.getAbsolutePath());
p.command("C:\\Program Files (x86)\\...\\program.exe","Enqueue","%1",next.getAbsolutePath());
p.command("C:\\Program Files (x86)\\...\\program.exe","Enqueue","\"%1\"",next.getAbsolutePath());
p.command("C:\\Program Files (x86)\\...\\program.exe","/Enqueue","\"%1\"",next.getAbsolutePath());
"Not working" in this case meaning that the program is launched, but nothing happens (the file is not even opened).
If I switch them around in this order: (program, file, parameters) then the file is opened correctly, but the additional parameters do nothing, as if they weren't even there.
What is the correct way to translate those parameters into the ProcessBuilder command?

The first thing you need to do, is make "C:\Program Files (x86)\...\program.exe" /Enqueue "%1" into an array of [C:\Program Files (x86)\...\program.exe, /Enqueue, %1] otherwise ProcessBuilder will try and execute the whole String as a single command, which really isn't what you want.
Maybe something like...
String cmd = "\"C:\\Program Files (x86)\\...\\program.exe\" /Enqueue \"%1\"";
StringBuilder sb = new StringBuilder(cmd);
List<String> commands = new ArrayList<>(10);
while (sb.length() > 0) {
if (sb.charAt(0) == '"') {
int nextIndex = sb.indexOf("\"", 1);
if (nextIndex < 0) {
nextIndex = sb.length();
} else {
nextIndex++;
}
commands.add(sb.substring(1, nextIndex).replace("\"", ""));
sb.delete(0, nextIndex);
} else if (sb.charAt(0) == ' ') {
if (sb.length() > 1 && sb.charAt(1) != '"') {
int nextIndex = sb.indexOf(" ", 1);
if (nextIndex < 0) {
nextIndex = sb.length();
}
commands.add(sb.substring(1, nextIndex));
sb.delete(0, nextIndex);
} else {
sb.delete(0, 1);
}
}
}
System.out.println(commands);
Which will print...
[C:\Program Files (x86)\...\program.exe, /Enqueue, %1]
There's probably a really neat regular expression you could use to help with this, but this will get the job done, more or less.
Next, you want to replace %1 with the file you want to open. Now, you can do this within the previous code, which would be more efficient, but for demonstration purposes...
String[] parameters = {"Hello kitty"};
for (int index = 0; index < commands.size(); index++) {
String value = commands.get(index);
if (value.startsWith("%")) {
int parameter = Integer.parseInt(value.substring(1)) - 1;
if (parameter < parameters.length) {
commands.set(index, parameters[parameter]);
}
// You might want to think about what you want to do if you have
// more parameter marks then you do have actual parameter values
}
}
System.out.println(commands);
Which prints out...
[C:\Program Files (x86)\...\program.exe, /Enqueue, Hello kitty]
Which you can now pass to ProcessBuilder, for example...
ProcessBuilder pb = new ProcessBuilder(commands);
Now, you could do the String substitution at many different points in the code, in many different ways, this is just an example of one

Related

Extract zip and re-zip with password in java

I am trying to extract list of zip files from folder and then re-zipping them with password. The problem is while re-zipping, the iteration/loop is not stopping. Also, re-zipped files should be a separate zip file each rather than merging all contents to one zip.
Here's what I have tried:
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import net.lingala.zip4j.core.ZipFile;
import net.lingala.zip4j.exception.ZipException;
import net.lingala.zip4j.model.ZipParameters;
import net.lingala.zip4j.util.Zip4jConstants;
public class AddFilesWithAESEncryption2 {
public AddFilesWithAESEncryption2() {
try {
//Extract Zip files as folders
try {
String ZipSourcePath = "E:/EZipTest/";
String ExtractTo = "D:/DZipTest/";
String files1;
File folder1 = new File(ZipSourcePath);
File[] listOfFiles1 = folder1.listFiles();
for (int i = 0; i < listOfFiles1.length; i++) {
if (listOfFiles1[i].isFile()) {
files1 = listOfFiles1[i].getName();
String ZipFiles = "E:/EZipTest/" + files1;
try {
ZipFile zipFile = new ZipFile(ZipFiles);
List fileHeaderList = zipFile.getFileHeaders();
zipFile.extractAll(ExtractTo);
} catch (ZipException e) {
e.printStackTrace();
}
}
}
//Get list of folders
String DirectoryNames;
String ExtractedDirectories1 = "D:/DZipTest/";
File folder2 = new File(ExtractedDirectories1);
File[] listOfFiles2 = folder2.listFiles();
for (int i = 0; i < listOfFiles2.length; i++) {
if (listOfFiles2[i].isDirectory()) {
DirectoryNames = listOfFiles2[i].getName();
String ListOfDirectories = "D:/DZipTest/" + DirectoryNames;
//Get list of files
String ExtractedDirectories = ListOfDirectories;
File folder3 = new File(ExtractedDirectories);
File[] listOfFiles3 = folder3.listFiles();
for (int j = 0; j < listOfFiles3.length; j++) {
File file = listOfFiles3[j];
if (file.isFile()) {
String FileNames = file.getName();
System.out.println(ListOfDirectories + FileNames);
//Compress and zip the files
ZipFile zipFile = new ZipFile("D:/" + listOfFiles2[i].getName() + ".zip");
ArrayList filesToAdd = new ArrayList();
filesToAdd.add(new File(ListOfDirectories + FileNames));
ZipParameters parameters = new ZipParameters();
parameters.setCompressionMethod(Zip4jConstants.COMP_DEFLATE); // set compression method to deflate compression
parameters.setCompressionLevel(Zip4jConstants.DEFLATE_LEVEL_NORMAL);
parameters.setEncryptFiles(true);
parameters.setEncryptionMethod(Zip4jConstants.ENC_METHOD_AES);
parameters.setAesKeyStrength(Zip4jConstants.AES_STRENGTH_256);
parameters.setPassword("test");
zipFile.addFiles(filesToAdd, parameters);
}
}
}
}
} catch (ZipException e) {
e.printStackTrace();
}
} catch (Exception e) {
e.printStackTrace();
}
}
public static void main(String[] args) {
new AddFilesWithAESEncryption2();
}
}
Refactoring
Refactoring your code will help you understand what it does. It will reveal the problem and immediately identify a fix. Here's how it goes. Note that this is not a complete tutorial, but I hope you get the point.
First, extract a nice method that does the unzipping. Mark everything inside the first for loop, then right click and choose Refactor / Extract Method.... Name it unzipFile. Note that you now have a nice small, potentially reusable and potentially testable (JUnit) method.
Next, mark everything from ZipParameters parameters to parameters.setPassword("test"); Right click, Refactor / Extract Method.... Name it getEncryptionParameters. Note how 7 lines of code have been removed from the long method and readability is improved.
Right click on parameters and choose Refactor / Inline .... Note how the temporary variable disappears.
See the bug
If you have followed closely, there is a piece of code like this:
//Compress and zip the files
ZipFile zipFile = new ZipFile("D:/" + listOfFiles2[i].getName() + ".zip");
ArrayList filesToAdd = new ArrayList();
filesToAdd.add(new File(ListOfDirectories + FileNames));
zipFile.addFiles(filesToAdd, getEncryptionParameters());
See what it does? It creates a new ZIP file, adds only one file to filesToAdd and that's it. But why? It says FileNames. How can that be one file only?
Looking at
String FileNames = file.getName();
that's really just one file, so the variable name is wrong.
Right click FileNames and choose Refactor/Rename.... Enter fileName. Note how the variable name in your program matches to what it really is. It heavily improves readability of the code.
Simplify
Now that you know you're adding only one file, use addFile() instead of addFiles(). You're getting rid of the ArrayList:
//Compress and zip the files
ZipFile zipFile = new ZipFile("D:/" + listOfFiles2[i].getName() + ".zip");
File fileToAdd = new File(ListOfDirectories + fileName);
zipFile.addFile(fileToAdd, getEncryptionParameters());
Fix the bug
As spotted before, a new ZipFile(...) is created inside the loop and only one file is added to it. Move that line out of the loop pressing Alt+Up.
Continue refactoring
A part of the problem is already fixed (I haven't tried, actually), but your code is still not error-free. Let's go on:
Mark everything from File[] listOfFiles3 to the end of the for loop that follows. Right click, Refactor/Extract Method..., name it rezip. Your big method becomes smaller again.
Right click on ExtractedDirectories, Refactor / Inline .... You just got rid of a unnecessary temporary variable.
See something? Your code should look like this:
//Get list of files
File folder3 = new File(ListOfDirectories);
rezip(listOfFiles2, i, ListOfDirectories, folder3);
Note how folder3 and ListOfDirectories is essentially the same. Let's get rid of it. Move the line File folder3 = new File(ListOfDirectories); into the method, just behind private void rezip(...){ and remove the parameter File folder3 from both, the method call and the method declaration of rezip().
The loop using rezip() now looks like this:
for (int i = 0; i < listOfFiles2.length; i++) {
if (listOfFiles2[i].isDirectory()) {
DirectoryNames = listOfFiles2[i].getName();
String ListOfDirectories = "D:/DZipTest/" + DirectoryNames;
rezip(listOfFiles2, i, ListOfDirectories);
}
}
You might spot that DirectoryNames is actually just one, not many. Right click, Refactor/Rename.... Enter subDirectory.
Right click subDirectory, Refactor / Inline .... Read the error message. Right click References / Workspace. Check the results and find out that this variable is only used within the for loop. Delete the declaration outside and declare it at its first use. Now do the Refactor / Inline ... operation.
Your code looks like this:
for (int i = 0; i < listOfFiles2.length; i++) {
if (listOfFiles2[i].isDirectory()) {
String ListOfDirectories = "D:/DZipTest/" + listOfFiles2[i].getName();
rezip(listOfFiles2, i, ListOfDirectories);
}
}
Again, there's a variable name indicating a list or an Array, but that's not true. Refactor / Rename..., name it directoryToZip.
Inline the following variables in this order: ExtractedDirectories1, folder2, ZipSourcePath, folder1.
Rename in this order listOfFiles1 to zipFiles and listOfFiles2 to extractedDirectories.
Remove files1 since it is never used.
The final bug
The method is now short and readable enough to understand it completely. Does the following make sense?
String ExtractTo = "D:/DZipTest/";
File[] zipFiles = new File("E:/EZipTest/").listFiles();
for (int i = 0; i < zipFiles.length; i++) {
unzipFile(ExtractTo, zipFiles, i);
}
File[] extractedDirectories = new File("D:/DZipTest/").listFiles();
for (int i = 0; i < extractedDirectories.length; i++) {
if (extractedDirectories[i].isDirectory()) {
String directoryToZip = "D:/DZipTest/" + extractedDirectories[i].getName();
rezip(extractedDirectories, i, directoryToZip);
}
}
No it doesn't.
You don't want to extract all archives first but one by one
You don't want to zip subdirectories, you want to zip everything in the ExtractTo directory
Fixing the final bug
The signature of unzipFile() does not look right. If it unzips one file only as the name suggests, why does it get access to all files then?
Replace unzipFile(ExtractTo, zipFiles, i); by unzipFile(ExtractTo, zipFiles[i]);. This breaks the code. Eclipse will mark it red. Fix it by changing the parameters from
private void unzipFile(String ExtractTo, File[] listOfFiles1, int i)
to
private void unzipFile(String ExtractTo, File listOfFiles1)
Inside unzip, replace listOfFiles1[i] by listOfFiles1. Then Refactor/Rename... it to sourceZipFile.
Similar for the rezip method: it should get the directory to zip and the target file name only. Therefore change
rezip(extractedDirectories, i, directoryToZip);
to
rezip(extractedDirectories[i], directoryToZip);
Then adapt the method itself from
private void rezip(File[] listOfFiles2, int i, String ListOfDirectories) throws ZipException
to
private void rezip(File listOfFiles2, String ListOfDirectories) throws ZipException
then change listOfFiles2[i] to listOfFiles2. Rename it to targetFile.
Now you have a nice unzipFile() method and a rezip() method. Let's combine it in a cool way:
String ExtractTo = "D:/DZipTest/";
File[] zipFiles = new File("E:/EZipTest/").listFiles();
for (int i = 0; i < zipFiles.length; i++) {
unzipFile(ExtractTo, zipFiles[i]);
rezip(zipFiles[i], ExtractTo);
// TODO: delete extracted files here
}
Awesome, ain't it?
Notes
Maybe you've seen how much effort it is to understand your code and provide a fix. Actually, too much effort for Stack Overflow. Next time you ask a question, please try to provide code that is at minumum as readable as your code now.
The code is still not as clean as it should be. Spend some more time on it. When you think it's superb, post it on https://codereview.stackexchange.com/ to get even more instructions.

What is the most efficient way to list a directory?

So my title might not be the most descriptive but essentially I am doing this in one class:
Launching a perl script that creates a series of files
In another function within the same class, I would like to provision certain files that I have created from my first script into another script
//process 1
launchOnCommandLine("perl --arg1 -arg2");
What the above script does, is that it produces a bunch of files in the current working directory. In a second script what I want to be able to do is retrieve all the output files of extension (.example), retrieve their paths, concatenate them in a comma-seperated list, and feed it into a second script.
//process 2
launchSecondScript("perl --list-of-comma-seperated-file paths
What is the most efficient way to retrieve that comma-seperated string of file paths and provision it to a second function. I also know the directory where the files will be outputted so that is not a problem.
I might use a simple Java function to do this
private static String concatenateFilePaths(
String directory, String extension) {
StringBuilder sb = new StringBuilder();
File f = new File(directory);
if (f != null && f.isDirectory()) {
File[] files = f.listFiles();
for (File file : files) {
if (file != null
&& file.getName().endsWith(extension)) {
if (sb.length() > 0) {
sb.append(", ");
}
sb.append('"');
sb.append(file.getPath());
sb.append('"');
}
}
}
return sb.toString();
}
I then might use it like so
System.out.println(concatenateFilePaths("/tmp/test/",
".example"));
On my system ls /tmp/test
a.example b.example c
Result of above call
"/tmp/test/a.example", "/tmp/test/b.example"
Similar to Elliot's response here is a java7 (nio) version
public static String listFiles(String dir, String extensionToMatch){
StringBuilder fileList = new StringBuilder();
try (DirectoryStream<Path> directoryStream = Files.newDirectoryStream(Paths.get(dir))) {
for (Path path : directoryStream) {
if(path.toString().endsWith(extensionToMatch)){
if(fileList.length() != 0){
fileList.append(",");
}
fileList.append(path.toString());
}
}
} catch (IOException ex) {}
return fileList.toString();
}

filenames from catalog path in javafx

I want to get filnames from catalog path in Javafx 2 without extensions, but with filters (*.jpeg, *.jpg...).
Filenames should be as list of strings. But I don't know how can I do it.
If anybody know how do it, please for help.
For example manually manage such files
File f = new File("D:\\dir_name\\");
if (f.isDirectory()) {
String[] list = f.list();
for (int pos = 0; pos < list.length; pos++) {
if (list[pos].contains(".")//contains extension
&& list[pos].lastIndexOf(".") != (list[pos].length() - 1))//point is not last character
{
list[pos]=list[pos].substring(0,list[pos].lastIndexOf("."));
}
}
}

How to split directory path components in string in java [duplicate]

This question already has answers here:
Closed 10 years ago.
Possible Duplicate:
how to split the string in java
FileSystemView fsv = FileSystemView.getFileSystemView();
File[] roots = fsv.getRoots();
for (int i = 0; i < roots.length; i++)
{
System.out.println("Root: " + roots[i]);
}
System.out.println("Home directory: " + fsv.getHomeDirectory());
Root: C:\Users\RS\Desktop
Home directory: C:\Users\RS\Desktop
I want cut the root or Home Directory components like
String C, Users, RS, Desktop
I'd rather not succumb to the temptation of using split ona file name, when java has its own cleaner, cross-platform functions for path manipluation.
I think this basic pattern works from java 1.4 and onward:
File f = new File("c:\\Some\\Folder with spaces\\Or\\Other");
do {
System.out.println("Parent=" + f.getName());
f = f.getParentFile();
} while (f.getParentFile() != null);
System.out.println("Root=" + f.getPath());
Will output:
Path=Other
Path=Or
Path=Folder with spaces
Path=Some
Root=c:\
You probably want to use f.getCanonicalPath or f.getAbsolutePath first, so it also works with relative paths.
Unfortunately, this needs f.getPath for the root and f.getName for the other parts, and i create the parts in backward order.
UPDATE: You can compare f with fsv.getHomeDirectory() while scanning upward, and break when it turns out you were in a subdirectory of your home folder.
In the light of user844382 answer, this is the platform safe way for splitting the path:
String homePath = FileSystemView.getFileSystemView().getHomeDirectory().getAbsolutePath();
System.out.println(homePath);
System.out.println(Arrays.deepToString(homePath.split(Matcher.quoteReplacement(System.getProperty("file.separator")))));
}
On linux it outputs:
/home/isipka
[, home, isipka]
On windows it outputs:
C:\Documents and Settings\linski\Desktop
[C:, Documents and Settings, linski, Desktop]
If you omit the Matcher.quoteReplacement() method call, the code will fail on windows. This method handles the escaping of special characters like "\" (file separator on windows) and "$".
You could use java.nio.file.Path for this:
FileSystemView fsv = FileSystemView.getFileSystemView();
File[] roots = fsv.getRoots();
for (int i = 0; i < roots.length; i++)
{
System.out.println("Root: " + roots[i]);
Path p = roots[i].toPath();
for (int j=0; j < p.getNameCount(); j++)
System.out.println(p.getName(j));
}
System.out.println("Home directory: " + fsv.getHomeDirectory());
FileSystemView fsv = FileSystemView.getFileSystemView();
File[] roots = fsv.getRoots();
for (int i = 0; i < roots.length; i++) {
System.out.println("Root: " + roots[i]);
for (String s : roots[i].toString().split(":?\\\\")) {
System.out.println(s);
}
}
System.out.println("Home directory: " + fsv.getHomeDirectory());
Try using regex split root.split(":?\\\\")
A solution that is different than the others would be to get the name from the File API:
File file = roots[i];
while (file != null) {
if (file.getName().length() > 0) {
System.out.println(file.getName());
} else {
System.out.println(file.getPath().substring(0, 1));
}
file = file.getParentFile();
}
This solution returns the path in the reversed order, so you will have to do some small changes.
Try the String.split() method. http://docs.oracle.com/javase/1.4.2/docs/api/java/lang/String.html#split(java.lang.String)
It takes regular expressions when splitting, so it's possible to do some really advanced stuff. For you splitting by \\might do it.
Since \ adds functionality to regular expressions, we need to mark it as a character rather than the "regex operator". That explains the double .

I would like to check if the file name inserted by the user match the name of a the file in the directory

I would like to know how to check if a file exists in the directory or if the name of the file typed in by the user matches what is in the directory and if it dont i out put a certain msg.. I am using FileDialog. That is if the user types in the name of a file it loops through the directory
for a file with that name. If no match is found then a message should be printed.
FileDialog fileWindow =
new FileDialog(new Frame(), "Tash & David's File Founder System");
fileWindow.setVisible(true);
if (fileWindow.getFile() != null) {
File fileDirectory = new File(fileWindow.getDirectory());
String[] Directory = fileDirectory.list();
for (; i < Directory.length; i++) {
if(Directory[i].startsWith(fileWindow.getFile())){
System.out.print("yes");
}
}
}
else
JOptionPane.showConfirmDialog(null, "Are you Sure");
}
use exist() method. Read more
Use an .exists() method.
One method: java.io.File.exists() http://docs.oracle.com/javase/7/docs/api/java/io/File.html#exists%28%29
Another method: java.nio.file.Path.exists() http://docs.oracle.com/javase/7/docs/api/java/nio/file/Files.html#exists%28java.nio.file.Path,%20java.nio.file.LinkOption...%29
A simple Google search normally turns up results for easy questions such as this :)
Edit:
For simplicity (added all the code to check a file exists in the if()):
FileDialog fileWindow =
new FileDialog(new Frame(), "Tash & David's File Founder System");
fileWindow.setVisible(true);
if (fileWindow.getFile() != null && new File(fileWindow.getDirectory()+File.separator+fileWindow.getFile()).exists()) {
File fileDirectory = new File(fileWindow.getDirectory());
String[] Directory = fileDirectory.list();
for (; i < Directory.length; i++) {
if(Directory[i].startsWith(fileWindow.getFile())){
System.out.print("yes");
}
}
}
else
JOptionPane.showConfirmDialog(null, "Are you Sure");
}
You can optimise it, but you can see what needs to be done to check the existence of a file. Let me know if it works, and select this as a suitable answer :)

Categories