java.nio.file.Path.contains(subPath)? - java

I need to check whether a given path is a subpath anywhere within another path and was wondering whether there exists such a method already before writing my own.
Here is some code that may help to understand the problem:
Path someRealPath = Paths.get("/tmp/some/path/to/somewhere");
Path subpathToCheck = Paths.get("some/path");
// I am expecting something similar as follows:
someRealPath.contains(subPathToCheck) // should return true in this case
someRealPath.contains(Paths.get("some/to")) // should return false
I already saw relativize, but I don't think that's the easiest way to solve the problem. The simplest I came up with was normalize().toString().contains(/* other normalized string path */). But maybe there is an easier way? Lots of methods inside Paths look as if this functionality must already be in there. Maybe I am just not seeing it.

What I came up with is the following:
boolean containsSubPath(Path someRealPath, Path subPathToCheck) {
return someRealPath.normalize()
.toString()
.contains(subPathToCheck.normalize()
.toString());
}
This way I am able to just call:
containsSubPath(Paths.get("/tmp/some/path/to/somewhere"), Paths.get("some/path"));
As Thomas Kläger pointed out, this solution matches also paths, that are only substrings of a real path (which for my use case would also be ok).
Here is another solution that is probably more correct (for complete matching subpaths), but still not as short as I would like it to be (now corrected due to Kabhals input):
static boolean containsSubPath(Path realPath, Path subPath) {
for (Path realPathSegment : realPath.normalize()) {
if (containsSubPath(realPathSegment.iterator(), subPath.normalize().iterator())) {
return true;
}
}
return false;
}
private static boolean containsSubPath(Iterator<Path> realPathIterator, Iterator<Path> subPathIterator) {
var hasEntries = realPathIterator.hasNext() && subPathIterator.hasNext();
while (realPathIterator.hasNext() && subPathIterator.hasNext()) {
Path realPathSegment = realPathIterator.next();
Path subPathSegment = subPathIterator.next();
if (!Objects.equals(realPathSegment, subPathSegment))
return false;
}
return hasEntries;
}
Example calls with expected output:
containsSubPath(Paths.get("/tmp/some/path/to/somewhere"), Paths.get("some/path")) // true
containsSubPath(Paths.get("/tmp/some/path/to/somewhere"), Paths.get("me/pa")) // false
If you need to use it as BinaryOperator just use the method reference instead, i.e. YourClass::containsSubPath.

Related

modify underlying result/value of async object

I am using Kotlin in a webserver app and I have a line of code as follows:
.onComplete { jsonResult: AsyncResult<JsonObject>? ->
Now what I want to do is change the underlying JsonObject wrapped in the AsyncResult, so that it is going to be reflected further downstream.
var res: JsonObject? = jsonResult?.result()
if (res != null) {
if (res.getInteger("files_uploaded") > 0) {
res.put("URL", "Some URL")
}
}
I was then imagining to update the underlying JSON object in the result but not sure how to do that.
please take note that single quotes are missing and ` appear as \` because the code formatting. I tried to leave what seemed least confusing...
You should be able to make changes in the conditional statement
if (res !=null) {
res being the JsonObject:
console.log(res);
would show you what's in there. You may need to use
let resXmodifiedX = JSON.parse(res);
One approach is to write a function and pass res to that function which you can do if it is in the console.log(res).
Some notes on what's below:
place the function somewhere consistent maybe at the bottom of the file...
objects often have multiple levels res.person.name, res.contact.email, or whatever...
use multiple for loops:
let level = res[key]; for(child in level) {
you don't need to do this if you know exactly what object attributes you need to update.
you can set the value directly but you always want to test for it before trying to set it to avoid errors that stop execution.
let toBe = toBe =>`${toBe}`;
let update = (res)?toBe(update(res)):toBe('not Found');
This option is really only if you know for sure that data will be there and you can't proceed without it. Which is not uncommon but also not how JSON is designed to be used.
The code below is a concise way to make some simple changes but may not be an ideal solution. To use it xModify(res) replaces console.log(res) above.
function xModify(x) {
let resXmodifiedX = JSON.parse(x);
let res = resXmodifiedX;
for (key in res) {
res[key] = key=='name'? \`My change ${res[key]}\`: key=='other'? \`My Change ${res[key]}\`:res[key];
resXmodifiedX = JSON.stringify(res);
return resXmodifiedX;
}
That will update res.name and res.other otherwise res[key] is unchanged. If you do not need to parse res change let res = xModifiedx; to let res = x; remove the first line and change the last two lines to return res;
function xModify(x) {
let res = x;
for (key in res) {
res[key] = key=='name'? \`My change ${res[key]}\`: key=='other'? \`My Change ${res[key]}\`:res[key];
return res;
}
If your data is numeric which is not generally the case in a web server response scenario this is a terrible approach. Because it is probably a string I used the template variable as a way to easily add a complex pattern in place of a string. My change ${res[key]} not a real world example. Any valid JS code can go in the ${ } (template variable). I've been defaulting to the first pattern more and more.
let me = (bestCase)?`${'the best version'} of myself`:`${'someone'} I'm ok with`;

Java boolean "Unexpected return value"

I'm new to Java and I can't understand why the IDE says that "Unexpected return value" inside the forEach where I declared that the boolean is true or false by an If statement.
My goal is to check that there is an object inside the "States" HashMap which already uses the name that I want to set to a new state. (The HashMap's key is a String which is called IdentifierOfState and the value is my State object which contains variables like its name.) Thank you for your help in advance!
public boolean isStateNameClaimed(String NameOfState)
{
States.forEach((IdentifierOfState, ValueOfState) ->
{
if (ValueOfState.getNameOfState().equalsIgnoreCase(NameOfState)) {return true;}
else {return false;}
});
return false;
}
The problem is that you are attempting to return the results in the wrong place. The {return true;} and {return true;} are in a lambda, so they are attempting to return a result for the lambda. But the inferred type signature for that lambda doesn't allow any values to be returned.
If your intention is that those return statements should be returning a result from isStateNameClaimed, then the better solution is to just use a for loop to iterate the elements of States.
It doesn't help things that your Java code contains a number of egregious Java style violations. You should NOT start the name of a variable with an upper-case letter. It will confuse ... and then annoy ... other people reading your code.
You may say: "Nah, I don't need to follow the rules, 'cos nobody else will be reading my code". But you are asking >>us<< to read your code.
I'm new to Java ...
... so NOW is the time to learn to do it correctly. Java style matters to people reading your code.
This is how I would write it in classic Java:
public boolean isStateNameClaimed(String name) {
for (State v: states.values()) {
if (v.getNameOfState().equalsIgnoreCase(name)) {
return true;
} else {
return false;
}
}
return false;
}
Or using streams:
public boolean isStateNameClaimed(String name) {
return states.values().stream().anyMatch(
(v) -> v.getNameOfState().equalsIgnoreCase(name));
}
Actually ... I just noticed that those two solutions are not equivalent. And based on your description of what you are trying to do, it probably means that the first one and your original attempt are algorithmically incorrect.
forEach will invoke a given callable function for every element. We can't have return value to that function.
Try using "filter" or assign result to local variable.
Return from lambda forEach() in java

Shortening if() with string.equals method

Is there any way to shorten this if() statement? To avoid repeating string.equals() somehow?
if (extension.equals("jpg") || extension.equals("JPG") || extension.equals("png") || extension.equals("PNG") || extension.equals("bmp") || extension.equals("BMP") || extension.equals("jpeg") || extension.equals("JPEG"))
{
tmp.setIcon(new ImageIcon(getClass().getResource("/menage/Resources/imageIco.png")));
}
To something looking similar to this :
if (extension.equals(("jpg")||("JPG")||("png")||("PNG")||("bmp")||("BMP")||("jpeg")||("JPEG")))
{
tmp.setIcon(new ImageIcon(getClass().getResource("/menage/Resources/imageIco.png"));)
}
I am aware that this question looks odd, however if() with such long conditions list is unclear and requires a lot of writing as well.
Start by changing equals(...) to equalsIgnoreCase(...).
Other options, create a HashSet of lower case Strings (or upper case if desired) with your image extensions and see if it contains your String of interest, changed to lower case:
if (imageExtSet.contains(myExtension.toLowerCase()) {
}
Here is short version with predefined image types:
Set<String> imgTypes = new HashSet<>() {{
add("jpg"); add("JPG");
add("png"); add("PNG");
add("bmp"); add("BMP");
add("jpeg"); add("JPEG");
}};
public boolean isImgType(String type) {
return imgTypes.contains(type);
}
You can keep all values in a list and then asks if contains. If it's only a one liner (you don't need to ask for this condition anywhere else), you can do:
if (Arrays.asList("jpg", "JPG", "png", "PNG", "bmp", "BMP", "jpeg", "JPEG").contains(extension))
You can of course save the list as an object and then anywhere you need to ask for this condition reference it.
Use HashSet
Like this
Set<String> extSet= new HashSet<String>();
// Add All in Lower case .. to save your efforts
extSet.add("jpg");
extSet.add("png");
//...etc etc
and just check if it is present in the Set
if(extSet.contains(extension==null?null:extension.toLowerCase()))
{
/// True
}
else
{
// False
}
One thing you can do to eliminate some checks, is to convert the string to lower case:
String ext = extension.toLowerCase();
Now you have shorten the statement to:
if (ext.equals("jpg") || ext.equals("png") || ext.equals("bmp") || ext.equals("jpeg"))
if (Arrays.asList("jpg", "jpeg", "png", "bmp").contains(extension.toLowerCase))
The other answers give lots of good low-level ideas, but the basic principle here is to prevent code reuse.
If you are doing this test more than once, create a method that does the test for you:
boolean isValidImageExtenstion(String extension) {
return (extension.equals("jpg") || extension.equals("JPG") ||
extension.equals("png") || extension.equals("PNG") ||
extension.equals("bmp") || extension.equals("BMP") ||
extension.equals("jpeg") || extension.equals("JPEG"));
}
Call the method whenever you need it. If you like you can use one of the approaches described in the other answers within the method, (and the 'ignore case' suggestion is certainly worth it) but the rest become less important now that you have prevented the code repetition. As a bonus, if you decide you want to support gif extensions you only have to make the change in one place.
The advantages of this approach over the others are that it is self-documenting. It's pretty obvious what the method does, and some of the other answers are pretty obscure.
If you are only doing this once, and don't intend to do it again, then you have already created working code, so don't waste your time modifying working code.
Add in some methods...
private static boolean isJpeg(String ext) {
return java.util.Arrays.asList("jpg", "jpeg").contains(ext.toLowerCase());
}
private static boolean isPng(String ext) {
return "png".equalsIgnoreCase(ext);
}
private static boolean isBmp(String ext) {
return "bmp".equalsIgnoreCase(ext);
}
And change it to...
else if (isJpeg(extension) || isPng(extension) || isBmp(extension))
{
tmp.setIcon(new ImageIcon(getClass().getResource("/menage/Resources/imageIco.png")));
}
The isJpeg will throw a NullPointerException if the extention is null, so ensure it's not null by adding extension != null || ... or something.
The above is slightly different for your specific case as it allows JpEg and all other mixed capitalizations to slip through. If you don't want that, use these. Plus, the below have the added benefit of never throwing NullPointerException if the extension is null.
private static boolean isJpeg(String ext) {
return java.util.Arrays.asList("jpg", "JPG", "jpeg", "JPEG").contains(ext);
}
private static boolean isPng(String ext) {
return java.util.Arrays.asList("png", "PNG").contains(ext);
}
private static boolean isBmp(String ext) {
return java.util.Arrays.asList("bmp", "BMP").contains(ext);
}

How to check whether a (String) location is a valid saving path in Java?

I am receiving a string from user which should be used as a location to save content to a file. This string should contain enough information, like a directory + file name.
My question is, how can I check whether the provided string is a valid path to save content to a file (at least in theory)?
It does not matter whether directories are created or not, or whether one has proper access to the location itself. I am only interested in checking the structure of the provided string.
How should I proceed? I was thinking about creating a File object, then extracting its URI. Is there any better way?
You can use File.getCanonicalPath() to validate according the current OS rules.
import java.io.File;
import java.io.IOException;
public class FileUtils {
public static boolean isFilenameValid(String file) {
File f = new File(file);
try {
f.getCanonicalPath();
return true;
}
catch (IOException e) {
return false;
}
}
public static void main(String args[]) throws Exception {
// true
System.out.println(FileUtils.isFilenameValid("well.txt"));
System.out.println(FileUtils.isFilenameValid("well well.txt"));
System.out.println(FileUtils.isFilenameValid(""));
//false
System.out.println(FileUtils.isFilenameValid("test.T*T"));
System.out.println(FileUtils.isFilenameValid("test|.TXT"));
System.out.println(FileUtils.isFilenameValid("te?st.TXT"));
System.out.println(FileUtils.isFilenameValid("con.TXT")); // windows
System.out.println(FileUtils.isFilenameValid("prn.TXT")); // windows
}
}
Have you looked at Apache Commons IO? This library includes various things for handling path information which may help e.g. FilenameUtils.getPath(String filename) which returns the path from a full filename.
Easiest: try to save, listen for exceptions.
The only time I'd do something more complicated would be if the writing was to be deferred, and you want to give the user his feedback now.
Here is what I have so far:
private static boolean checkLocation(String toCheck) {
// If null, we necessarily miss the directory section
if ( toCheck == null ) {
System.out.println("Missing directory section");
return false;
}
String retrName = new File(toCheck).toURI().toString();
// Are we dealing with a directory?
if ( retrName.charAt(retrName.length()-1) == '/') {
System.out.println("Missing file name");
return false;
}
return true;
}
This tells me whether I have a proper directory structure and whether I am pointing to a directory rather than a file. I do not need I/O access.
I have noticed that if I use the File.createNewFile() method on a location pointing explicitly to a directory (which does not exist yet), Java creates a file with no extension, which is plain wrong. Either it should create a directory or it should throw some kind of error.
Also, the File constructors tend to add the current directory if none is provided in the argument. It is not documented, but no real harm in my case.
If anyone has a better solution, I'll approve it.
EDIT
I have finally combined the above with the input from RealHowTo.

Is there a way in Java to determine if a path is valid without attempting to create a file?

I need to determine if a user-supplied string is a valid file path (i.e., if createNewFile() will succeed or throw an Exception) but I don't want to bloat the file system with useless files, created just for validation purposes.
Is there a way to determine if the string I have is a valid file path without attempting to create the file?
I know the definition of "valid file path" varies depending on the OS, but I was wondering if there was any quick way of accepting C:/foo or /foo and rejecting banana.
A possible approach may be attempting to create the file and eventually deleting it if the creation succeeded, but I hope there is a more elegant way of achieving the same result.
Path class introduced in Java 7 adds new alternatives, like the following:
/**
* <pre>
* Checks if a string is a valid path.
* Null safe.
*
* Calling examples:
* isValidPath("c:/test"); //returns true
* isValidPath("c:/te:t"); //returns false
* isValidPath("c:/te?t"); //returns false
* isValidPath("c/te*t"); //returns false
* isValidPath("good.txt"); //returns true
* isValidPath("not|good.txt"); //returns false
* isValidPath("not:good.txt"); //returns false
* </pre>
*/
public static boolean isValidPath(String path) {
try {
Paths.get(path);
} catch (InvalidPathException | NullPointerException ex) {
return false;
}
return true;
}
Edit:
Note Ferrybig's
comment : "The only disallowed character in a file name on Linux is the NUL character, this does work under Linux."
This would check for the existance of the directory as well.
File file = new File("c:\\cygwin\\cygwin.bat");
if (!file.isDirectory())
file = file.getParentFile();
if (file.exists()){
...
}
It seems like file.canWrite() does not give you a clear indication if you have permissions to write to the directory.
File.getCanonicalPath() is quite useful for this purpose. IO exceptions are thrown for certain types of invalid filenames (e.g. CON, PRN, *?* in Windows) when resolving against the OS or file system. However, this only serves as a preliminary check; you will still need to handle other failures when actually creating the file (e.g. insufficient permissions, lack of drive space, security restrictions).
A number of things can go wrong when you try and create a file:
Your lack the requisite permissions;
There is not enough space on the device;
The device experiences an error;
Some policy of custom security prohibits you from creating a file of a particular type;
etc.
More to the point, those can change between when you try and query to see if you can and when you actually can. In a multithreaded environment this is one of the primary causes of race conditions and can be a real vulnerability of some programs.
Basically you just have to try and create it and see if it works. And that's the correct way to do it. It's why things like ConcurrentHashMap has a putIfAbsent() so the check and insert is an atomic operation and doesn't suffer from race conditions. Exactly the same principle is in play here.
If this is just part of some diagnostic or install process, just do it and see if it works. Again there's no guarantee that it'll work later however.
Basically your program has to be robust enough to die gracefully if it can't write a relevant file.
boolean canWrite(File file) {
if (file.exists()) {
return file.canWrite();
}
else {
try {
file.createNewFile();
file.delete();
return true;
}
catch (Exception e) {
return false;
}
}
}
Here's something you can do that works across operating systems
Using regex match to check for existing known invalid characters.
if (newName.matches(".*[/\n\r\t\0\f`?*\\<>|\":].*")) {
System.out.println("Invalid!");
} else {
System.out.println("Valid!");
}
Pros
This works across operating systems
You can customize it whatever way
you want by editing that regex.
Cons
This might not be a complete list and need more research to fill in more invalid patterns or characters.
Just do it (and clean up after yourself)
A possible approach may be attempting to create the file and eventually deleting it if the creation succeeded, but I hope there is a more elegant way of achieving the same result.
Maybe that's the most robust way.
Below is canCreateOrIsWritable that determines whether your program is able to create a file and its parent directories at a given path, or, if there's already a file there, write to it.
It does so by actually creating the necessary parent directories as well as an empty file at the path. Afterwards, it deletes them (if there existed a file at the path, it's left alone).
Here's how you might use it:
var myFile = new File("/home/me/maybe/write/here.log")
if (canCreateOrIsWritable(myFile)) {
// We're good. Create the file or append to it
createParents(myFile);
appendOrCreate(myFile, "new content");
} else {
// Let's pick another destination. Maybe the OS's temporary directory:
var tempDir = System.getProperty("java.io.tmpdir");
var alternative = Paths.get(tempDir, "second_choice.log");
appendOrCreate(alternative, "new content in temporary directory");
}
The essential method with a few helper methods:
static boolean canCreateOrIsWritable(File file) {
boolean canCreateOrIsWritable;
// The non-existent ancestor directories of the file.
// The file's parent directory is first
List<File> parentDirsToCreate = getParentDirsToCreate(file);
// Create the parent directories that don't exist, starting with the one
// highest up in the file system hierarchy (closest to root, farthest
// away from the file)
reverse(parentDirsToCreate).forEach(File::mkdir);
try {
boolean wasCreated = file.createNewFile();
if (wasCreated) {
canCreateOrIsWritable = true;
// Remove the file and its parent dirs that didn't exist before
file.delete();
parentDirsToCreate.forEach(File::delete);
} else {
// There was already a file at the path → Let's see if we can
// write to it
canCreateOrIsWritable = java.nio.file.Files.isWritable(file.toPath());
}
} catch (IOException e) {
// File creation failed
canCreateOrIsWritable = false;
}
return canCreateOrIsWritable;
}
static List<File> getParentDirsToCreate(File file) {
var parentsToCreate = new ArrayList<File>();
File parent = file.getParentFile();
while (parent != null && !parent.exists()) {
parentsToCreate.add(parent);
parent = parent.getParentFile();
}
return parentsToCreate;
}
static <T> List<T> reverse(List<T> input) {
var reversed = new ArrayList<T>();
for (int i = input.size() - 1; i >= 0; i--) {
reversed.add(input.get(i));
}
return reversed;
}
static void createParents(File file) {
File parent = file.getParentFile();
if (parent != null) {
parent.mkdirs();
}
}
Keep in mind that between calling canCreateOrIsWritable and creating the actual file, the contents and permissions of your file system might have changed.

Categories

Resources