Java PathMatcher: GLOB patterns are inconsistent on UNIX/Windows - java

Background:
I was writing a filewalker that starts at a specific folder and walks through each subfolder and its contents. Each time a directory or file is visited I compare the path to a PathMatcher containing a glob pattern (directories matched on path, files matched on extension). If it matched the pattern I would add it to a list for later use. This is so that I can build a filtered list based on a GLOB pattern. I wrote this on Unix (mac) and it seemed to work fine. When I tested it on Windows I got 0 results.
Filter(String dirPattern, String extensionPattern) {
dirMatcher = FileSystems.getDefault().getPathMatcher(
"glob:" + dirPattern);
extensionMatcher = FileSystems.getDefault().getPathMatcher(
"glob:" + extensionPattern);
}
Problem:
Take these GLOB patterns for example
Directory pattern:
**/{foo/bar,foo/baz/,fizz/buzz/fizzbuzz}
Would work fine on UNIX but not on Windows. Now I've read that the JVM shouldn't have trouble interpreting '/' as a valid file seperator for both platforms but nonetheless I tried changing it to a Windows style path.
**\{foo\bar,foo\baz\,fizz\buzz\fizzbuzz}
Needs some escaping for java:
**\\{foo\\bar,foo\\baz\\,fizz\\buzz\\fizzbuzz}
But this still wont match anything...
I even tried doing an OS check and replacing the '/' with Java File.seperator.
if (OSUtil.getCurrentOS() == OS.Windows) {
dirPattern = dirPattern.replace("/", File.separator);
extensionPattern = extensionPattern
.replace("/", File.separator);
}
It seems like the file seperators between the brackets are interpreted and correct but the first slash needs to be Windows style. At this point I'm not sure what is happening. Is there anyone that can shed some light on this problem? Any help would be appreciated.

Related

Is there a way to get the path of a File with "/" instead of "\"?

I am making an HTTP Server in Java so that (on start) it finds all files in a directory (and it's sub-directories) and adds them to the server. But when getting the path of a file and trying to give it to HttpServer.createContext(), it throws a java.lang.IllegalArgumentException: Illegal value for path or protocol. (with the string argument, say "\folder/index.html"). To get this value, I used
file.getParent().substring(24) + "/" + file.getName()
I used substring because I had to exclude the folder the web server is in. The illegal character is the backslash. I have tried extending File to change separator and separatorChar, but that only created 2 new variables. While using String.replace() didn't seem to have any effect. Is there a different method than File.getParent or File.getPath that I can use, or is there a way to use String.replace that I am not seeing?
EDIT:
String.replace() seems to be the best answer... But I am not completely sure how to use it.
EDIT 2: For some reason the backslash isn't showing up, so I changed it.
You have to use the java System.getProperty.
Notice that, in this context, "file.separator" is a key which we are
using to get this property from current system executing the java VM.
Insteady of using a slash (/), you should choose a platform agnostic file separator, as an example it should be:
String separator = System.getProperty("file.separator");
System.out.println(separator);
// unix / , windows \
Have a look at Paths.get(...)
Try Paths.get(".") // current working directory.
Or tell it, on which path it should start:
Use System.getProperty("user.dir"), for current loged in user, home directory.
String pathStr = "/";
Path homeDir = Paths.get(System.getProperty("user.dir"))
Getting from the user directory into the data directory: homeDir.get("data")
Path dataPath = Paths.get(System.getProperty("user.dir"));
File dataFile = dataPath.toFile();
Now use operations on dataFile, to check what files and directories there are, on that location of the file system.

Convert Windows style path into unix path in java code

I am working in a java code that was designed to run on windows and contains a lot of references to files using windows style paths "System.getProperty("user.dir")\trash\blah". I am in charge to adapt it and deploy in linux. Is there an efficient way to convert all those paths(\) to unix style (/) like in "System.getProperty("user.dir")/trash/blah". Maybe, some configuration in java or linux to use \ as /.
My approach is to use the Path object to hold the path information, handle concatenate and relative path. Then, call Path's toString() to get the path String.
For converting the path separator, I preferred to use the apache common io library's FilenameUtils. It provides the three usefule functions:
String separatorsToSystem(String path);
String separatorsToUnix(String path);
String separatorsToWindows(String path)
Please look the code snippet, for relative path, toString, and separator changes:
private String getRelativePathString(String volume, Path path) {
Path volumePath = Paths.get(configuration.getPathForVolume(volume));
Path relativePath = volumePath.relativize(path);
return FilenameUtils.separatorsToUnix(relativePath.toString());
}
I reread your question and realize you likely don't need help writing paths. For what you're trying to do I am not able to find a solution. When I did this in a project recently I had to take time to convert all paths. Further, I made the assumption that working out of the "user.home" as a root directory was relatively sure to include write access for that user running my application. In any case, here are some path problems I addressed.
I rewrote the original Windows code like so:
String windowsPath = "C:\temp\directory"; //no permission or non-existing in osx or linux
String otherWindowsPath = System.getProperty("user.home") + "\Documents\AppFolder";
String multiPlatformPath = System.getProperty("user.home") + File.separator + "Documents" + File.separator + "AppFolder";
If you're going to be doing this in a lot of different places, perhaps write a utility class and override the toString() method to give you your unix path over and over again.
String otherWindowsPath = System.getProperty("user.home") + "\Documents\AppFolder";
otherWindowsPath.replace("\\", File.separator);
Write a script, replace all "\\" with a single forward slash, which Java will convert to the respected OS path.

Concatenating Strings in Java generates between-in null

This question looks like very similar to: Concatenating null strings in Java
But my issue is some different.
I want to build an absolute path to a file:
String path = properties.get("path"); // returns /home/myuser/relativepath/ , ends with bar /
String file = currentFile; // currentFile values "file.txt"
String result = path + file; // this results in /home/myuser/relativepath/nullfile.txt
Why is there than 'null' text? That's the reason my application does not work now.
I have review it in Windows and Linux.
In Windows it works perfectly.
In Linux, I have this issue.
I uploaded properties file and then, edited with vi command.
Maybe is this the problem?
Shouldn't I use this way to generate an absolute path, and use File.Separator property in Java?
EDIT: I have post my final right answer with detailed steps. I hope it would be useful.
My bet (though I have not seen Java behave this way) is there's a null-ish character (such as a carriage return) of some sort in your properties file which Windows handles at the OS level so Java/Properties doesn't see it.
As a first pass, try printing the length and last few characters of your path string, e.g.:
for(int i = Math.max(0, path.length()-5); i < path.length(); i++) {
System.out.print(path.charAt(i)+":"+((int)path.charAt(i))+" ");
}
System.out.println(path.length());
Willing to bet the last character, on Linux, is not what you'd expect. The right fix would then be to clean up your properties file so that it's compatible on both OSes.
Well, the complete and detailed steps to fix my issue are these (maybe any of them could not be necessary, but I prefer to write them all):
Create config file in Linux with vi, emacs, ... (not upload file from Windows).
Edit file with vi, emacs... At the end of each path, do not include directory separator character ( / ).
Check variables before contatenate them. Make sure they don't have any space and other unexpected character.
Concatenate variables with:
String result = path + File.separator + file;
I hope this would be useful. Thank you all for your suggestions.
Regards
What do you expect?
Yo´re doing a String result = path + result;
int a = 1 + a would be similar... don´t use a variable to init itself.
(That can´t be your code in the first place, if you´re getting this output.)
result is path+file :
String path = properties.get("path");
String file = currentFile;
String result = path + file;
^
change here
the result is: /home/myuser/relativepath/file.txt

java FilenameFilter regex (look ahead)

I am trying to filter files using FilenameFilter to grep files files in a directory.
% ls -1
DirFilter.class
DirList.class
DirList.java
doctors.txt
node.l
rels.l
I am trying to filter node.l and rels.l. Filter should succeed if and only if both files are present.
I tried my regex on debuggex.com and it seems to work as expected :
http://www.debuggex.com/embed/CZgVeUE2iWsNfRNG
my regex : (?s)node.l.?(?=(rels.l))
but when I run it through DirList.java filter it doesn't work..
% java DirList "(?s)node.l.?(?=(rels.l))"
<no-output>
Now I am using DirList.java from Thinking in Java
http://www.cs.odu.edu/~cs476/tijava2/c10/DirList.java
Any ideas?
DirList is evaluating your regex against each file name separately, not as a single \n delimited directory listing string as returned by ls. Your regex will never match under those conditions since it never sees more than one file name at a time.
FilenameFilter works for single file not for groups of files so regex will be applied only to that single file. Instead of regex try maybe this way:
take name of file
if it is node.l check if new File(currentDir+"/rels.l").exists().
if it is rels.l check if new File(currentDir+"/node.l").exists().

Java's File.toString or Path.toString with a specific path separator

I am developing a Scala application on Windows, and I need to insert a path to a file into an HTML template. I use Java's io and nio to work with files and paths.
/* The paths actually come from the environment. */
val includesPath = Paths.get("foo\\inc")
val destinationPath = Paths.get("bar\\dest")
/* relativeIncludesPath.toString == "..\\foo\\inc", as expected */
val relativeIncludesPath = destinationPath.relativize(includesPath)
The problem is that the output of relativeIncludesPath.toString contains backslashes \ as separators - because the application runs on Windows - but since the path is to be inserted into a HTML template, it must contain forward slashes / instead.
Since I couldn't find anything like file/path.toStringUsingSeparator('/') in the docs, I am currently helping myself with relativeIncludesPath.toString.replace('\\', '/'), which I find rather unappealing.
Question: Is there really no better way than using replace?
I also experimented with Java's URI, but it's relativize is incomplete.
The Windows implementation of the Path interface stores the path internally as a string (at least in the OpenJDK implementation) and simply returns that representation when toString() is called. This means that no computation is involved and there is no chance to "configure" any path separator.
For this reason, I conclude that your solution is the currently the best option to solve your problem.
I just ran into this issue. If you have a relative path, you can use the fact that Path is an Iterable<Path> of its elements, following an optional initial root element, and then can concatenate the pieces yourself with forward slashes. Unfortunately the root element can contain slashes, e.g. in Windows you get root elements like c:\ and \\foo\bar\ (for UNC paths), so it seems like no matter what you still have to replace with forward slashes. But you could do something like this...
static public String pathToPortableString(Path p)
{
StringBuilder sb = new StringBuilder();
boolean first = true;
Path root = p.getRoot();
if (root != null)
{
sb.append(root.toString().replace('\\','/'));
/* root elements appear to contain their
* own ending separator, so we don't set "first" to false
*/
}
for (Path element : p)
{
if (first)
first = false;
else
sb.append("/");
sb.append(element.toString());
}
return sb.toString();
}
and when I test it with this code:
static public void doit(String rawpath)
{
File f = new File(rawpath);
Path p = f.toPath();
System.out.println("Path: "+p.toString());
System.out.println(" "+pathToPortableString(p));
}
static public void main(String[] args) {
doit("\\\\quux\\foo\\bar\\baz.pdf");
doit("c:\\foo\\bar\\baz.pdf");
doit("\\foo\\bar\\baz.pdf");
doit("foo\\bar\\baz.pdf");
doit("bar\\baz.pdf");
doit("bar\\");
doit("bar");
}
I get this:
Path: \\quux\foo\bar\baz.pdf
//quux/foo/bar/baz.pdf
Path: c:\foo\bar\baz.pdf
c:/foo/bar/baz.pdf
Path: \foo\bar\baz.pdf
/foo/bar/baz.pdf
Path: foo\bar\baz.pdf
foo/bar/baz.pdf
Path: bar\baz.pdf
bar/baz.pdf
Path: bar
bar
Path: bar
bar
The textual substitution of backslash with forward slash is definitely easier, but I have no idea whether it would break some devious edge case. (Can there be backslashes in Unix paths?)
Building on the iterator idea (my problem is similar: need the path for a web page, but it has to be relative and start with a slash)
StringBuilder foo = new StringBuilder();
relative.iterator().forEachRemaining(part -> foo.append("/").append(part));
foo will contain the appropriate path.
You can fetch most of the system properties in Java. Take a look at this link:
http://docs.oracle.com/javase/tutorial/essential/environment/sysprop.html
You want this:
Key: "file.separator"
Meaning: Character that separates components of a file path. This is "/" on UNIX and "\" on Windows.
String sep = System.getProperty("path.separator");

Categories

Resources