Skip to content

Commit 531aaa5

Browse files
committed
[IO-291] Add new function FileUtils.directoryContains.
git-svn-id: https://svn.apache.org/repos/asf/commons/proper/io/trunk@1201713 13f79535-47bb-0310-9956-ffa450edef68
1 parent ec04894 commit 531aaa5

3 files changed

Lines changed: 213 additions & 0 deletions

File tree

src/changes/changes.xml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ The <action> type attribute can be add,update,fix,remove.
4040

4141
<body>
4242
<release version="2.1.1" date="TBA">
43+
<action dev="sebb" type="add" issue="IO-291" due-to="ggregory">
44+
Add new function FileUtils.directoryContains.
45+
</action>
4346
<action dev="sebb" type="add" issue="IO-275" due-to="CJ Aspromgos">
4447
FileUtils.contentEquals and IOUtils.contentEquals - Add option to ignore "line endings"
4548
Added contentEqualsIgnoreEOL methods to both classes

src/main/java/org/apache/commons/io/FileUtils.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1429,6 +1429,55 @@ public static boolean deleteQuietly(File file) {
14291429
}
14301430
}
14311431

1432+
/**
1433+
* Determines whether the {@code parent} directory contains the {@code child} element (a file or directory).
1434+
* <p>
1435+
* Files are normalized before comparison.
1436+
* </p>
1437+
*
1438+
* Edge cases:
1439+
* <ul>
1440+
* <li>A {@code directory} must not be null: if null, throw IllegalArgumentException</li>
1441+
* <li>A {@code directory} must be a directory: if not a directory, throw IllegalArgumentException</li>
1442+
* <li>A directory does not contain itself: return false</li>
1443+
* <li>A null child file is not contained in any parent: return false</li>
1444+
* </ul>
1445+
*
1446+
* @param directory
1447+
* the file to consider as the parent.
1448+
* @param child
1449+
* the file to consider as the child.
1450+
* @return true is the candidate leaf is under by the specified composite. False otherwise.
1451+
* @throws IOException
1452+
* if an IO error occurs while checking the files.
1453+
* @since 2.2
1454+
*/
1455+
public static boolean directoryContains(final File directory, final File child) throws IOException {
1456+
1457+
// Fail fast against NullPointerException
1458+
if (directory == null) {
1459+
throw new IllegalArgumentException("Directory must not be null");
1460+
}
1461+
1462+
if (!directory.isDirectory()) {
1463+
throw new IllegalArgumentException("Not a directory: " + directory);
1464+
}
1465+
1466+
if (child == null) {
1467+
return false;
1468+
}
1469+
1470+
// Canonicalize paths (normalizes relative paths)
1471+
String canonicalParent = directory.getCanonicalPath();
1472+
String canonicalChild = child.getCanonicalPath();
1473+
1474+
if (IOCase.SYSTEM.checkEquals(canonicalParent, canonicalChild)) {
1475+
return false;
1476+
}
1477+
1478+
return IOCase.SYSTEM.checkStartsWith(canonicalChild, canonicalParent);
1479+
}
1480+
14321481
/**
14331482
* Cleans a directory without deleting it.
14341483
*
@@ -2546,4 +2595,5 @@ public static boolean isSymlink(File file) throws IOException {
25462595
return true;
25472596
}
25482597
}
2598+
25492599
}
Lines changed: 160 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,160 @@
1+
/*
2+
* Licensed to the Apache Software Foundation (ASF) under one or more
3+
* contributor license agreements. See the NOTICE file distributed with
4+
* this work for additional information regarding copyright ownership.
5+
* The ASF licenses this file to You under the Apache License, Version 2.0
6+
* (the "License"); you may not use this file except in compliance with
7+
* the License. You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
package org.apache.commons.io;
18+
19+
import org.apache.commons.io.testtools.FileBasedTestCase;
20+
import org.junit.Test;
21+
22+
import java.io.File;
23+
import java.io.IOException;
24+
25+
/**
26+
* This class ensure the correctness of {@link FileUtils#directoryContains(File,File)}.
27+
*
28+
* @see FileUtils#directoryContains(File, File)
29+
* @since 2.2
30+
* @version $Id$
31+
*/
32+
public class FileUtilsDirectoryContainsTestCase extends FileBasedTestCase {
33+
34+
private File directory1;
35+
private File directory2;
36+
private File directory3;
37+
private File file1;
38+
private File file1ByRelativeDirectory2;
39+
private File file2;
40+
private File file2ByRelativeDirectory1;
41+
private File file3;
42+
final File top = getTestDirectory();
43+
44+
public FileUtilsDirectoryContainsTestCase(String name) {
45+
super(name);
46+
}
47+
48+
@Override
49+
protected void setUp() throws Exception {
50+
top.mkdirs();
51+
52+
directory1 = new File(top, "directory1");
53+
directory2 = new File(top, "directory2");
54+
directory3 = new File(directory2, "directory3");
55+
56+
directory1.mkdir();
57+
directory2.mkdir();
58+
directory3.mkdir();
59+
60+
file1 = new File(directory1, "file1");
61+
file2 = new File(directory2, "file2");
62+
file3 = new File(top, "file3");
63+
64+
// Tests case with relative path
65+
file1ByRelativeDirectory2 = new File(getTestDirectory(), "directory2/../directory1/file1");
66+
file2ByRelativeDirectory1 = new File(getTestDirectory(), "directory1/../directory2/file2");
67+
68+
FileUtils.touch(file1);
69+
FileUtils.touch(file2);
70+
FileUtils.touch(file3);
71+
}
72+
73+
@Override
74+
protected void tearDown() throws Exception {
75+
FileUtils.deleteDirectory(top);
76+
}
77+
78+
@Test
79+
public void testCanonicalPath() throws IOException {
80+
assertTrue(FileUtils.directoryContains(directory1, file1ByRelativeDirectory2));
81+
assertTrue(FileUtils.directoryContains(directory2, file2ByRelativeDirectory1));
82+
83+
assertFalse(FileUtils.directoryContains(directory1, file2ByRelativeDirectory1));
84+
assertFalse(FileUtils.directoryContains(directory2, file1ByRelativeDirectory2));
85+
}
86+
87+
@Test
88+
public void testDirectoryContainsDirectory() throws IOException {
89+
assertTrue(FileUtils.directoryContains(top, directory1));
90+
assertTrue(FileUtils.directoryContains(top, directory2));
91+
assertTrue(FileUtils.directoryContains(top, directory3));
92+
assertTrue(FileUtils.directoryContains(directory2, directory3));
93+
}
94+
95+
@Test
96+
public void testDirectoryContainsFile() throws IOException {
97+
assertTrue(FileUtils.directoryContains(directory1, file1));
98+
assertTrue(FileUtils.directoryContains(directory2, file2));
99+
}
100+
101+
@Test
102+
public void testDirectoryDoesNotContainFile() throws IOException {
103+
assertFalse(FileUtils.directoryContains(directory1, file2));
104+
assertFalse(FileUtils.directoryContains(directory2, file1));
105+
106+
assertFalse(FileUtils.directoryContains(directory1, file3));
107+
assertFalse(FileUtils.directoryContains(directory2, file3));
108+
}
109+
110+
@Test
111+
public void testDirectoryDoesNotContainsDirectory() throws IOException {
112+
assertFalse(FileUtils.directoryContains(directory1, top));
113+
assertFalse(FileUtils.directoryContains(directory2, top));
114+
assertFalse(FileUtils.directoryContains(directory3, top));
115+
assertFalse(FileUtils.directoryContains(directory3, directory2));
116+
}
117+
118+
@Test
119+
public void testDirectoryDoesNotExist() throws IOException {
120+
final File dir = new File("DOESNOTEXIST");
121+
assertFalse(dir.exists());
122+
try {
123+
assertFalse(FileUtils.directoryContains(dir, file1));
124+
fail("Expected " + IllegalArgumentException.class.getName());
125+
} catch (IllegalArgumentException e) {
126+
// expected
127+
}
128+
}
129+
130+
@Test
131+
public void testSameFile() throws IOException {
132+
try {
133+
assertTrue(FileUtils.directoryContains(file1, file1));
134+
fail("Expected " + IllegalArgumentException.class.getName());
135+
} catch (IllegalArgumentException e) {
136+
// expected
137+
}
138+
}
139+
140+
@Test
141+
public void testFileDoesNotExist() throws IOException {
142+
assertFalse(FileUtils.directoryContains(top, null));
143+
final File file = new File("DOESNOTEXIST");
144+
assertFalse(file.exists());
145+
assertFalse(FileUtils.directoryContains(top, file));
146+
}
147+
148+
@Test
149+
public void testUnrealizedContainment() throws IOException {
150+
final File dir = new File("DOESNOTEXIST");
151+
final File file = new File(dir, "DOESNOTEXIST2");
152+
assertFalse(dir.exists());
153+
assertFalse(file.exists());
154+
try {
155+
assertTrue(FileUtils.directoryContains(dir, file));
156+
} catch (IllegalArgumentException e) {
157+
// expected
158+
}
159+
}
160+
}

0 commit comments

Comments
 (0)