Skip to content

Commit 1ed7cd5

Browse files
Babcock, ScottBabcock, Scott
authored andcommitted
Merge pull request #3 in MFATT/common from pr/add-file-utils to master
* commit '3be5a9067fc8b392950a17055ee85b7870192044': Fix bugs Fix assertion message copy/paste issues Fix test method name New PathUtils class with unit tests
2 parents cf88cc2 + 3be5a90 commit 1ed7cd5

File tree

2 files changed

+176
-0
lines changed

2 files changed

+176
-0
lines changed
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package com.nordstrom.common.file;
2+
3+
import java.io.File;
4+
import java.io.IOException;
5+
import java.nio.file.FileSystems;
6+
import java.nio.file.Files;
7+
import java.nio.file.Path;
8+
import java.nio.file.PathMatcher;
9+
import java.util.Comparator;
10+
import java.util.Objects;
11+
import java.util.Optional;
12+
import java.util.regex.Matcher;
13+
import java.util.regex.Pattern;
14+
import java.util.stream.Stream;
15+
16+
public class PathUtils {
17+
18+
private PathUtils() {
19+
throw new AssertionError("PathUtils is a static utility class that cannot be instantiated");
20+
}
21+
22+
/**
23+
* Get the next available path in sequence for the specified base name and extension in the specified folder.
24+
*
25+
* @param targetPath path to target directory for the next available path in sequence
26+
* @param baseName base name for the path sequence
27+
* @param extension extension for the path sequence
28+
* @return the next available path in sequence
29+
* @throws IOException if an I/O error is thrown when accessing the starting file.
30+
*/
31+
public static Path getNextPath(Path targetPath, String baseName, String extension) throws IOException {
32+
String newName;
33+
34+
Objects.requireNonNull(targetPath, "[targetPath] must be non-null");
35+
Objects.requireNonNull(baseName, "[baseName] must be non-null");
36+
Objects.requireNonNull(extension, "[extension] must be non-null");
37+
38+
File targetFile = targetPath.toFile();
39+
if ( ! (targetFile.exists() && targetFile.isDirectory())) {
40+
throw new IllegalArgumentException("[targetPath] must specify an existing directory");
41+
}
42+
if (baseName.isEmpty()) {
43+
throw new IllegalArgumentException("[baseName] must specify a non-empty string");
44+
}
45+
if (extension.isEmpty()) {
46+
throw new IllegalArgumentException("[extension] must specify a non-empty string");
47+
}
48+
49+
PathMatcher pathMatcher = FileSystems.getDefault().getPathMatcher("glob:" + baseName + "*." + extension);
50+
51+
try (Stream<Path> stream = Files.walk(targetPath, 1)) {
52+
int ext = extension.length() + 1;
53+
54+
Optional<String> optional =
55+
stream
56+
.map(Path::getFileName)
57+
.filter(pathMatcher::matches)
58+
.map(String::valueOf)
59+
.map(s -> s.substring(0, s.length() - ext))
60+
.sorted(Comparator.reverseOrder())
61+
.findFirst();
62+
63+
if (optional.isPresent()) {
64+
int index = 1;
65+
Pattern pattern = Pattern.compile(baseName + "-(\\d+)");
66+
Matcher matcher = pattern.matcher(optional.get());
67+
if (matcher.matches()) {
68+
index = Integer.parseInt(matcher.group(1));
69+
}
70+
newName = baseName + "-" + Integer.toString(index + 1) + "." + extension;
71+
} else {
72+
newName = baseName + "." + extension;
73+
}
74+
}
75+
76+
return targetPath.resolve(newName);
77+
}
78+
79+
}
Lines changed: 97 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,97 @@
1+
package com.nordstrom.common.file;
2+
3+
import static org.testng.Assert.assertEquals;
4+
5+
import java.io.File;
6+
import java.io.IOException;
7+
import java.lang.reflect.Constructor;
8+
import java.lang.reflect.InvocationTargetException;
9+
import java.lang.reflect.Modifier;
10+
import java.nio.file.Files;
11+
import java.nio.file.Path;
12+
import java.nio.file.Paths;
13+
14+
import org.testng.ITestContext;
15+
import org.testng.ITestResult;
16+
import org.testng.Reporter;
17+
import org.testng.annotations.Test;
18+
19+
public class PathUtilsTest {
20+
21+
@Test
22+
public void testNextPath() throws IOException {
23+
Path outputDir = getOutputPath();
24+
Path targetPath = outputDir.resolve("targetPath");
25+
if (targetPath.toFile().exists()) {
26+
for (File file : targetPath.toFile().listFiles()) {
27+
file.delete();
28+
}
29+
} else {
30+
Files.createDirectories(targetPath);
31+
}
32+
33+
Path path1 = PathUtils.getNextPath(targetPath, "test", "txt");
34+
assertEquals(path1.getFileName().toString(), "test.txt");
35+
36+
path1.toFile().createNewFile();
37+
Path path2 = PathUtils.getNextPath(targetPath, "test", "txt");
38+
assertEquals(path2.getFileName().toString(), "test-2.txt");
39+
}
40+
41+
private Path getOutputPath() {
42+
ITestResult testResult = Reporter.getCurrentTestResult();
43+
ITestContext testContext = testResult.getTestContext();
44+
String outputDirectory = testContext.getOutputDirectory();
45+
Path outputDir = Paths.get(outputDirectory);
46+
return outputDir;
47+
}
48+
49+
@Test(expectedExceptions = {AssertionError.class},
50+
expectedExceptionsMessageRegExp = "PathUtils is a static utility class that cannot be instantiated")
51+
public void testPrivateConstructor() throws Throwable {
52+
53+
Constructor<?>[] ctors;
54+
ctors = PathUtils.class.getDeclaredConstructors();
55+
assertEquals(ctors.length, 1, "PathUtils must have exactly one constructor");
56+
assertEquals(ctors[0].getModifiers() & Modifier.PRIVATE, Modifier.PRIVATE,
57+
"PathUtils constructor must be private");
58+
assertEquals(ctors[0].getParameterTypes().length, 0, "PathUtils constructor must have no arguments");
59+
60+
try {
61+
ctors[0].setAccessible(true);
62+
ctors[0].newInstance();
63+
} catch (InvocationTargetException e) {
64+
throw e.getCause();
65+
}
66+
}
67+
68+
@Test(expectedExceptions = {NullPointerException.class})
69+
public void testNullPath() throws IOException {
70+
PathUtils.getNextPath(null, "test", "txt");
71+
}
72+
73+
@Test(expectedExceptions = {IllegalArgumentException.class})
74+
public void testNonExistentPath() throws IOException {
75+
PathUtils.getNextPath(Paths.get("foobar"), "test", "txt");
76+
}
77+
78+
@Test(expectedExceptions = {NullPointerException.class})
79+
public void testNullBaseName() throws IOException {
80+
PathUtils.getNextPath(getOutputPath(), null, "txt");
81+
}
82+
83+
@Test(expectedExceptions = {IllegalArgumentException.class})
84+
public void testEmptyBaseName() throws IOException {
85+
PathUtils.getNextPath(getOutputPath(), "", "txt");
86+
}
87+
88+
@Test(expectedExceptions = {NullPointerException.class})
89+
public void testNullExtenstion() throws IOException {
90+
PathUtils.getNextPath(getOutputPath(), "test", null);
91+
}
92+
93+
@Test(expectedExceptions = {IllegalArgumentException.class})
94+
public void testEmptyExtension() throws IOException {
95+
PathUtils.getNextPath(getOutputPath(), "test", "");
96+
}
97+
}

0 commit comments

Comments
 (0)