I am trying to find if given path is possible child of another path using java. Both path may not exist.
Say c:\\Program Files\\My Company\\test\\My App
File parent = maybeChild.getParentFile();
while ( parent != null ) {
if ( parent.equals( possibleParent ) )
return true;
parent = parent.getParentFile();
}
return false;
maybeChild.getCanonicalPath().startsWith( possibleParent.getCanonicalPath() );
Asides from the fact the paths may not exist (and the canonicalisation may not succeed), this looks like a reasonable approach that should work in the straightforward case.
You may want to look at calling getParentFile() on the "maybe child" in a loop, testing if it matches the parent at each step. You can also short-circuit the comparison if the parent isn't a (real) directory.
Perhaps something like the following:
boolean myCheck(File maybeChild, File possibleParent) throws IOException
{
final File parent = possibleParent.getCanonicalFile();
if (!parent.exists() || !parent.isDirectory()) {
// this cannot possibly be the parent
return false;
}
File child = maybeChild.getCanonicalFile();
while (child != null) {
if (child.equals(parent)) {
return true;
}
child = child.getParentFile();
}
// No match found, and we've hit the root directory
return false;
}
Note that if you want the child relationship to be strict (i.e. a directory is not a child of itself) you can change the initial child
assignment on line 9 to be child.getParentFile()
so the first check happens on the child's containing directory.
That will probably work fine as it is, although I would use getCanonicalPath()
rather than getAbsolutePath()
. This should normalize any weird paths like x/../y/z
which would otherwise screw up the matching.
Old question but a pre-1.7 solution:
public boolean startsWith(String possibleRoot, String possibleChildOrSame) {
String[] possiblePath = new File(possibleRoot).getAbsolutePath().replace('\\', '/').split("/");
String[] possibleChildOrSamePath = new File(possibleChildOrSame).getAbsolutePath().replace('\\', '/').split("/");
if (possibleChildOrSamePath.length < possiblePath.length) {
return false;
}
// not ignoring case
for (int i = 0; i < possiblePath.length; i++) {
if (!possiblePath[i].equals(possibleChildOrSamePath[i])) {
return false;
}
}
return true;
}
For completeness the java 1.7+ solution:
public boolean startsWith(String possibleRoot, String possibleChildOrSame) {
Path p1 = Paths.get(possibleChildOrSame).toAbsolutePath();
Path p2 = Paths.get(possibleRoot).toAbsolutePath();
return p1.startsWith(p2);
}
Surprisingly there is no simple, yet functional solution.
The accepted answer does consider same directories as child, which is wrong.
Here is one using java.nio.file.Path API only:
static boolean isChildPath(Path parent, Path child){
Path pn = parent.normalize();
Path cn = child.normalize();
return cn.getNameCount() > pn.getNameCount() && cn.startsWith(pn);
}
Test cases:
@Test
public void testChildPath() {
assertThat(isChildPath(Paths.get("/FolderA/FolderB/F"), Paths.get("/FolderA/FolderB/F"))).isFalse();
assertThat(isChildPath(Paths.get("/FolderA/FolderB/F"), Paths.get("/FolderA/FolderB/F/A"))).isTrue();
assertThat(isChildPath(Paths.get("/FolderA/FolderB/F"), Paths.get("/FolderA/FolderB/F/A.txt"))).isTrue();
assertThat(isChildPath(Paths.get("/FolderA/FolderB/F"), Paths.get("/FolderA/FolderB/F/../A"))).isFalse();
assertThat(isChildPath(Paths.get("/FolderA/FolderB/F"), Paths.get("/FolderA/FolderB/FA"))).isFalse();
assertThat(isChildPath(Paths.get("FolderA"), Paths.get("FolderA"))).isFalse();
assertThat(isChildPath(Paths.get("FolderA"), Paths.get("FolderA/B"))).isTrue();
assertThat(isChildPath(Paths.get("FolderA"), Paths.get("FolderA/B"))).isTrue();
assertThat(isChildPath(Paths.get("FolderA"), Paths.get("FolderAB"))).isFalse();
assertThat(isChildPath(Paths.get("/FolderA/FolderB/F"), Paths.get("/FolderA/FolderB/F/Z/X/../A"))).isTrue();
}