FileUtil.isAncestor() performance

This commit is contained in:
Roman Shevchenko
2012-08-30 21:56:21 +04:00
parent 9dff2a94ba
commit e50c234299
3 changed files with 62 additions and 22 deletions
@@ -20,21 +20,39 @@ import com.intellij.util.ThrowableRunnable;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
public class FileUtilPerformanceTest {
private String myTestPath = "/a/b/c/./d///e/../f/g/h/i/j/";
private String myCanonicalPath = "/a/b/c/d/f/g/h/i/j";
@Test
public void toCanonicalPath() throws Exception {
final String testPath = "/a/b/c/./d///e/../f/g/h/i/j/";
assertEquals("/a/b/c/d/f/g/h/i/j", FileUtil.toCanonicalPath(testPath));
assertEquals(myCanonicalPath, FileUtil.toCanonicalPath(myTestPath));
PlatformTestUtil.startPerformanceTest("", 1000, new ThrowableRunnable() {
@Override
public void run() throws Throwable {
for (int i = 0; i < 1000000; ++i) {
final String canonicalPath = FileUtil.toCanonicalPath(testPath, '/');
final String canonicalPath = FileUtil.toCanonicalPath(myTestPath, '/');
assert canonicalPath != null && canonicalPath.length() == 18 : canonicalPath;
}
}
}).cpuBound().assertTiming();
}
@Test
public void isAncestor() throws Exception {
assertTrue(FileUtil.isAncestor(myTestPath, myCanonicalPath, false));
PlatformTestUtil.startPerformanceTest("", 4000, new ThrowableRunnable() {
@Override
public void run() throws Throwable {
for (int i = 0; i < 1000000; ++i) {
assert FileUtil.isAncestor(myTestPath, myCanonicalPath, false);
assert !FileUtil.isAncestor(myTestPath, myCanonicalPath, true);
}
}
}).cpuBound().assertTiming();
}
}
@@ -99,31 +99,34 @@ public class FileUtil extends FileUtilRt {
return new File(path).isAbsolute();
}
public static boolean isAncestor(@NotNull final String ancestor, @NotNull final String descendant, final boolean strict) {
return isAncestor(new File(toSystemDependentName(ancestor)), new File(toSystemDependentName(descendant)), strict);
}
/**
* Check if the {@code ancestor} is an ancestor of {@code file}.
*
* @param ancestor the file
* @param file the file
* @param strict if {@code false} then this method returns {@code true} if {@code ancestor}
* and {@code file} are equal
* @return {@code true} if {@code ancestor} is parent of {@code file}; {@code false} otherwise
* @param ancestor supposed ancestor.
* @param file supposed descendant.
* @param strict if {@code false} then this method returns {@code true} if {@code ancestor} equals to {@code file}.
* @return {@code true} if {@code ancestor} is parent of {@code file}; {@code false} otherwise.
*/
public static boolean isAncestor(@NotNull File ancestor, @NotNull File file, boolean strict) {
File parent = strict ? getParentFile(file) : file;
while (true) {
if (parent == null) {
return false;
}
// Do not user file.equals as it incorrectly works on MacOS
if (pathsEqual(parent.getPath(), ancestor.getPath())) {
return true;
}
parent = getParentFile(parent);
return isAncestor(ancestor.getPath(), file.getPath(), strict);
}
public static boolean isAncestor(@NotNull String ancestor, @NotNull String descendant, boolean strict) {
String ancestorPath = toCanonicalPath(ancestor);
String filePath = toCanonicalPath(descendant);
if (ancestorPath == null || filePath == null) {
return false;
}
boolean startsWith = SystemInfo.isFileSystemCaseSensitive ? StringUtil.startsWith(filePath, ancestorPath)
: StringUtil.startsWithIgnoreCase(filePath, ancestorPath);
if (!startsWith) {
return false;
}
return filePath.length() > ancestorPath.length() && filePath.charAt(ancestorPath.length()) == '/' ||
!strict && filePath.length() == ancestorPath.length();
}
/**
@@ -15,9 +15,12 @@
*/
package com.intellij.openapi.util.io;
import com.intellij.openapi.util.SystemInfo;
import org.junit.Test;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
import static org.junit.Assert.assertTrue;
/**
* @author Roman Shevchenko
@@ -57,4 +60,20 @@ public class FileUtilTest {
assertEquals("c:/", FileUtil.toCanonicalPath("c:\\a\\..\\..", WINDOWS_SEPARATOR));
assertEquals("c:/b", FileUtil.toCanonicalPath("c:\\a\\..\\..\\b", WINDOWS_SEPARATOR));
}
@Test
public void isAncestor() throws Exception {
assertTrue(FileUtil.isAncestor("/a/b/c", "/a/b/c/d/e/f", true));
assertTrue(FileUtil.isAncestor("/a/b/c/", "/a/b/c/d/e/f", true));
assertFalse(FileUtil.isAncestor("/a/b/c/1", "/a/b/c/2", true));
assertFalse(FileUtil.isAncestor("/a/b/c/1", "/a/b/c/2", false));
assertTrue(FileUtil.isAncestor("/a/b/c/", "/a/b/c", false));
assertTrue(FileUtil.isAncestor("/a///b/c", "/a/b/c/", false));
assertFalse(FileUtil.isAncestor("/a/b/c/", "/a/./b/c", true));
assertFalse(FileUtil.isAncestor("/a/b/c", "/a/b/c/", true));
assertFalse(FileUtil.isAncestor("/a/b/c", "/a/b/cde", true));
assertFalse(FileUtil.isAncestor("/a/b/c", "/a/b/cde", false));
assertEquals(!SystemInfo.isFileSystemCaseSensitive, FileUtil.isAncestor("/a/b/c", "/a/B/c/d", true));
}
}