Skip to content

Commit

Permalink
[IO-632] Add PathUtils for operations on NIO Path.
Browse files Browse the repository at this point in the history
Fix test class name and all delete(Path). 100% JaCoCo coverage for the
whole package.
  • Loading branch information
Gary Gregory committed Oct 11, 2019
1 parent 6ce3e00 commit 4373928
Show file tree
Hide file tree
Showing 8 changed files with 442 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.concurrent.atomic.AtomicLong;

/**
* Counts files, directories, and sizes, as a visit proceeds.
Expand All @@ -31,17 +30,15 @@
*/
public class CountingPathFileVisitor extends SimplePathFileVisitor {

private final AtomicLong byteCount = new AtomicLong();
private final AtomicLong directoryCount = new AtomicLong();
private final AtomicLong fileCount = new AtomicLong();
private final PathCounts pathCounts = new PathCounts();

/**
* Gets the byte count of visited files.
*
* @return the byte count of visited files.
*/
public long getByteCount() {
return this.byteCount.get();
return this.pathCounts.byteCount.get();
}

/**
Expand All @@ -50,7 +47,7 @@ public long getByteCount() {
* @return the count of visited directories.
*/
public long getDirectoryCount() {
return this.directoryCount.get();
return this.pathCounts.directoryCount.get();
}

/**
Expand All @@ -59,26 +56,34 @@ public long getDirectoryCount() {
* @return the byte count of visited files.
*/
public long getFileCount() {
return this.fileCount.get();
return this.pathCounts.fileCount.get();
}

/**
* Gets the visitation counts.
*
* @return the visitation counts.
*/
public PathCounts getPathCounts() {
return pathCounts;
}

@Override
public FileVisitResult postVisitDirectory(final Path dir, final IOException exc) throws IOException {
directoryCount.incrementAndGet();
pathCounts.directoryCount.incrementAndGet();
return FileVisitResult.CONTINUE;
}

@Override
public String toString() {
return String.format("%,d files in %,d directories for %,d bytes", Long.valueOf(fileCount.longValue()),
Long.valueOf(directoryCount.longValue()), Long.valueOf(byteCount.longValue()));
return pathCounts.toString();
}

@Override
public FileVisitResult visitFile(final Path file, final BasicFileAttributes attrs) throws IOException {
if (Files.exists(file)) {
fileCount.incrementAndGet();
byteCount.addAndGet(attrs.size());
pathCounts.fileCount.incrementAndGet();
pathCounts.byteCount.addAndGet(attrs.size());
}
return FileVisitResult.CONTINUE;
}
Expand Down
66 changes: 66 additions & 0 deletions src/main/java/org/apache/commons/io/file/PathCounts.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.commons.io.file;

import java.util.concurrent.atomic.AtomicLong;

/**
* Counts files, directories, and sizes, as a visit proceeds.
*
* @since 2.7
*/
public class PathCounts {

final AtomicLong byteCount = new AtomicLong();
final AtomicLong directoryCount = new AtomicLong();
final AtomicLong fileCount = new AtomicLong();

/**
* Gets the byte count of visited files.
*
* @return the byte count of visited files.
*/
public long getByteCount() {
return this.byteCount.get();
}

/**
* Gets the count of visited directories.
*
* @return the count of visited directories.
*/
public long getDirectoryCount() {
return this.directoryCount.get();
}

/**
* Gets the count of visited files.
*
* @return the byte count of visited files.
*/
public long getFileCount() {
return this.fileCount.get();
}

@Override
public String toString() {
return String.format("%,d files, %,d directories, %,d bytes", Long.valueOf(fileCount.longValue()),
Long.valueOf(directoryCount.longValue()), Long.valueOf(byteCount.longValue()));
}

}
56 changes: 51 additions & 5 deletions src/main/java/org/apache/commons/io/file/PathUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import java.nio.file.DirectoryStream;
import java.nio.file.FileVisitor;
import java.nio.file.Files;
import java.nio.file.NotDirectoryException;
import java.nio.file.Path;

/**
Expand All @@ -37,19 +38,61 @@ public final class PathUtils {
* @return The visitor used to count the given directory.
* @throws IOException if an I/O error is thrown by a visitor method.
*/
public static CountingPathFileVisitor countDirectory(final Path directory) throws IOException {
return visitFileTree(directory, new CountingPathFileVisitor());
public static PathCounts countDirectory(final Path directory) throws IOException {
return visitFileTree(directory, new CountingPathFileVisitor()).getPathCounts();
}


/**
* Deletes a file or directory. If the path is a directory, delete it and all sub-directories.
* <p>
* The difference between File.delete() and this method are:
* </p>
* <ul>
* <li>A directory to delete does not have to be empty.</li>
* <li>You get exceptions when a file or directory cannot be deleted; {@link java.io.File#delete()} returns a
* boolean.
* </ul>
*
* @param path file or directory to delete, must not be {@code null}
* @return The visitor used to delete the given directory.
* @throws NullPointerException if the directory is {@code null}
* @throws IOException if an I/O error is thrown by a visitor method or if an I/O error occurs.
*/
public static PathCounts delete(final Path path) throws IOException {
return Files.isDirectory(path) ? deleteDirectory(path) : deleteFile(path);
}

/**
* Deletes the given file.
*
* @param file The file to delete.
* @return A visitor with path counts set to 1 file, 0 directories, and the size of the deleted file.
* @throws IOException if an I/O error occurs.
* @throws NotDirectoryException if the file is a directory.
*/
public static PathCounts deleteFile(final Path file) throws IOException {
if (Files.isDirectory(file)) {
throw new NotDirectoryException(file.toString());
}
final PathCounts pathCounts = new PathCounts();
final long size = Files.exists(file) ? Files.size(file) : 0;
if (Files.deleteIfExists(file)) {
pathCounts.fileCount.set(1);
pathCounts.directoryCount.set(0);
pathCounts.byteCount.set(size);
}
return pathCounts;
}

/**
* Deletes a directory including sub-directories.
*
* @param directory directory to delete.
* @return The visitor used to delete the given directory.
* @throws IOException if an I/O error is thrown by a visitor method.
*/
public static DeletingPathFileVisitor deleteDirectory(final Path directory) throws IOException {
return visitFileTree(directory, new DeletingPathFileVisitor());
public static PathCounts deleteDirectory(final Path directory) throws IOException {
return visitFileTree(directory, new DeletingPathFileVisitor()).getPathCounts();
}

/**
Expand Down Expand Up @@ -108,6 +151,9 @@ public static <T extends FileVisitor<? super Path>> T visitFileTree(final Path d
return visitor;
}

/**
* Does allow to instantiate.
*/
private PathUtils() {
// do not instantiate.
}
Expand Down
38 changes: 38 additions & 0 deletions src/test/java/org/apache/commons/io/file/PathCountsTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You under the Apache License, Version 2.0
* (the "License"); you may not use this file except in compliance with
* the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package org.apache.commons.io.file;

import org.junit.jupiter.api.Assertions;
import org.junit.jupiter.api.Test;

public class PathCountsTest {

@Test
public void testCtor() {
final PathCounts pathCounts = new PathCounts();
Assertions.assertEquals(pathCounts.getByteCount(), 0);
Assertions.assertEquals(pathCounts.getDirectoryCount(), 0);
Assertions.assertEquals(pathCounts.getFileCount(), 0);
}

@Test
public void testToString() {
// Does not blow up
new PathCounts().toString();
}
}
26 changes: 13 additions & 13 deletions src/test/java/org/apache/commons/io/file/PathUtilsCountingTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -46,10 +46,10 @@ public class PathUtilsCountingTest {
public void testCountEmptyFolder() throws IOException {
final Path tempDirectory = Files.createTempDirectory(getClass().getCanonicalName());
try {
final CountingPathFileVisitor visitor = PathUtils.countDirectory(tempDirectory);
Assertions.assertEquals(1, visitor.getDirectoryCount());
Assertions.assertEquals(0, visitor.getFileCount());
Assertions.assertEquals(0, visitor.getByteCount());
final PathCounts pathCounts = PathUtils.countDirectory(tempDirectory);
Assertions.assertEquals(1, pathCounts.getDirectoryCount());
Assertions.assertEquals(0, pathCounts.getFileCount());
Assertions.assertEquals(0, pathCounts.getByteCount());
} finally {
Files.deleteIfExists(tempDirectory);
}
Expand All @@ -60,19 +60,19 @@ public void testCountEmptyFolder() throws IOException {
*/
@Test
public void testCountFolders1FileSize0() throws IOException {
final CountingPathFileVisitor visitor = PathUtils
final PathCounts pathCounts = PathUtils
.countDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-0"));
Assertions.assertEquals(1, visitor.getDirectoryCount());
Assertions.assertEquals(1, visitor.getFileCount());
Assertions.assertEquals(0, visitor.getByteCount());
Assertions.assertEquals(1, pathCounts.getDirectoryCount());
Assertions.assertEquals(1, pathCounts.getFileCount());
Assertions.assertEquals(0, pathCounts.getByteCount());
}

/**
* Tests a directory with one file of size 1.
*/
@Test
public void testCountFolders1FileSize1() throws IOException {
final CountingPathFileVisitor visitor = PathUtils
final PathCounts visitor = PathUtils
.countDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-1-file-size-1"));
Assertions.assertEquals(1, visitor.getDirectoryCount());
Assertions.assertEquals(1, visitor.getFileCount());
Expand All @@ -84,10 +84,10 @@ public void testCountFolders1FileSize1() throws IOException {
*/
@Test
public void testCountFolders2FileSize2() throws IOException {
final CountingPathFileVisitor visitor = PathUtils
final PathCounts pathCounts = PathUtils
.countDirectory(Paths.get("src/test/resources/org/apache/commons/io/dirs-2-file-size-2"));
Assertions.assertEquals(3, visitor.getDirectoryCount());
Assertions.assertEquals(2, visitor.getFileCount());
Assertions.assertEquals(2, visitor.getByteCount());
Assertions.assertEquals(3, pathCounts.getDirectoryCount());
Assertions.assertEquals(2, pathCounts.getFileCount());
Assertions.assertEquals(2, pathCounts.getByteCount());
}
}
Loading

0 comments on commit 4373928

Please sign in to comment.