Skip to content

Commit

Permalink
Roll forward.
Browse files Browse the repository at this point in the history
*** Original change description ***

Add MoreFiles.fileTraverser().

-------------
Created by MOE: https://github.com/google/moe
MOE_MIGRATED_REVID=175582408
  • Loading branch information
cgdecker authored and cpovirk committed Nov 13, 2017
1 parent f6cfba8 commit af7514a
Show file tree
Hide file tree
Showing 5 changed files with 172 additions and 7 deletions.
3 changes: 3 additions & 0 deletions android/guava/src/com/google/common/io/Files.java
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,9 @@ public String toString() {
* this case, iterables created by this traverser could contain files that are outside of the
* given directory or even be infinite if there is a symbolic link loop.
*
* <p>If available, consider using {@link MoreFiles#fileTraverser()} instead. It behaves the same
* except that it doesn't follow symbolic links and returns {@code Path} instances.
*
* <p>If the {@link File} passed to one of the {@link Traverser} methods does not exist or is not
* a directory, no exception will be thrown and the returned {@link Iterable} will contain a
* single element: that file.
Expand Down
119 changes: 119 additions & 0 deletions guava-tests/test/com/google/common/io/MoreFilesFileTraverserTest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
/*
* Copyright (C) 2017 The Guava Authors
*
* Licensed 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 com.google.common.io;

import static com.google.common.truth.Truth.assertThat;

import com.google.common.collect.Iterables;
import com.google.errorprone.annotations.CanIgnoreReturnValue;
import java.io.IOException;
import java.nio.file.Path;
import junit.framework.TestCase;

/**
* Tests for {@link MoreFiles#fileTraverser()}.
*
* @author Jens Nyman
*/

public class MoreFilesFileTraverserTest extends TestCase {

private Path rootDir;

@Override
public void setUp() throws IOException {
rootDir = Files.createTempDir().toPath();
}

@Override
public void tearDown() throws IOException {
MoreFiles.deleteRecursively(rootDir);
}

public void testFileTraverser_emptyDirectory() throws Exception {
assertThat(MoreFiles.fileTraverser().breadthFirst(rootDir)).containsExactly(rootDir);
}

public void testFileTraverser_nonExistingFile() throws Exception {
Path file = rootDir.resolve("file-that-doesnt-exist");

assertThat(MoreFiles.fileTraverser().breadthFirst(file)).containsExactly(file);
}

public void testFileTraverser_file() throws Exception {
Path file = newFile("some-file");

assertThat(MoreFiles.fileTraverser().breadthFirst(file)).containsExactly(file);
}

public void testFileTraverser_singleFile() throws Exception {
Path file = newFile("some-file");

assertThat(MoreFiles.fileTraverser().breadthFirst(rootDir)).containsExactly(rootDir, file);
}

public void testFileTraverser_singleDirectory() throws Exception {
Path file = newDir("some-dir");

assertThat(MoreFiles.fileTraverser().breadthFirst(rootDir)).containsExactly(rootDir, file);
}

public void testFileTraverser_multipleFilesAndDirectories() throws Exception {
Path fileA = newFile("file-a");
Path fileB = newFile("file-b");
Path dir1 = newDir("dir-1");
Path dir2 = newDir("dir-2");

assertThat(MoreFiles.fileTraverser().breadthFirst(rootDir))
.containsExactly(rootDir, fileA, fileB, dir1, dir2);
}

public void testFileTraverser_multipleDirectoryLayers_breadthFirstStartsWithTopLayer()
throws Exception {
Path fileA = newFile("file-a");
Path dir1 = newDir("dir-1");
newFile("dir-1/file-b");
newFile("dir-1/dir-2");

assertThat(Iterables.limit(MoreFiles.fileTraverser().breadthFirst(rootDir), 3))
.containsExactly(rootDir, fileA, dir1);
}

public void testFileTraverser_multipleDirectoryLayers_traversalReturnsAll() throws Exception {
Path fileA = newFile("file-a");
Path dir1 = newDir("dir-1");
Path fileB = newFile("dir-1/file-b");
Path dir2 = newFile("dir-1/dir-2");

assertThat(MoreFiles.fileTraverser().breadthFirst(rootDir))
.containsExactly(rootDir, fileA, fileB, dir1, dir2);
}

@CanIgnoreReturnValue
private Path newDir(String name) {
Path dir = rootDir.resolve(name);
dir.toFile().mkdir();
return dir;
}

@CanIgnoreReturnValue
private Path newFile(String name) throws IOException {
Path file = rootDir.resolve(name);
file.toFile().createNewFile();
return file;
}
}
2 changes: 2 additions & 0 deletions guava-tests/test/com/google/common/io/MoreFilesTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@
/**
* Tests for {@link MoreFiles}.
*
* <p>Note: {@link MoreFiles#fileTraverser()} is tested in {@link MoreFilesFileTraverserTest}.
*
* @author Colin Decker
*/

Expand Down
3 changes: 3 additions & 0 deletions guava/src/com/google/common/io/Files.java
Original file line number Diff line number Diff line change
Expand Up @@ -852,6 +852,9 @@ public String toString() {
* this case, iterables created by this traverser could contain files that are outside of the
* given directory or even be infinite if there is a symbolic link loop.
*
* <p>If available, consider using {@link MoreFiles#fileTraverser()} instead. It behaves the same
* except that it doesn't follow symbolic links and returns {@code Path} instances.
*
* <p>If the {@link File} passed to one of the {@link Traverser} methods does not exist or is not
* a directory, no exception will be thrown and the returned {@link Iterable} will contain a
* single element: that file.
Expand Down
52 changes: 45 additions & 7 deletions guava/src/com/google/common/io/MoreFiles.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,8 @@
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.TreeTraverser;
import com.google.common.graph.SuccessorsFunction;
import com.google.common.graph.Traverser;
import com.google.common.io.ByteSource.AsCharSource;
import com.google.j2objc.annotations.J2ObjCIncompatible;
import java.io.IOException;
Expand Down Expand Up @@ -291,16 +293,52 @@ private static final class DirectoryTreeTraverser extends TreeTraverser<Path> {

@Override
public Iterable<Path> children(Path dir) {
if (Files.isDirectory(dir, NOFOLLOW_LINKS)) {
try {
return listFiles(dir);
} catch (IOException e) {
// the exception thrown when iterating a DirectoryStream if an I/O exception occurs
throw new DirectoryIteratorException(e);
return fileTreeChildren(dir);
}
}

/**
* Returns a {@link Traverser} instance for the file and directory tree. The returned traverser
* starts from a {@link Path} and will return all files and directories it encounters.
*
* <p>The returned traverser attempts to avoid following symbolic links to directories. However,
* the traverser cannot guarantee that it will not follow symbolic links to directories as it is
* possible for a directory to be replaced with a symbolic link between checking if the file is a
* directory and actually reading the contents of that directory.
*
* <p>If the {@link Path} passed to one of the traversal methods does not exist or is not a
* directory, no exception will be thrown and the returned {@link Iterable} will contain a single
* element: that path.
*
* <p>{@link DirectoryIteratorException} may be thrown when iterating {@link Iterable} instances
* created by this traverser if an {@link IOException} is thrown by a call to {@link
* #listFiles(Path)}.
*
* <p>Example: {@code MoreFiles.fileTraverser().breadthFirst("/")} may return files with the
* following paths: {@code ["/", "/etc", "/home", "/usr", "/etc/config.txt", "/etc/fonts", ...]}
*/
public static Traverser<Path> fileTraverser() {
return Traverser.forTree(FILE_TREE);
}

private static final SuccessorsFunction<Path> FILE_TREE =
new SuccessorsFunction<Path>() {
@Override
public Iterable<Path> successors(Path path) {
return fileTreeChildren(path);
}
};

private static Iterable<Path> fileTreeChildren(Path dir) {
if (Files.isDirectory(dir, NOFOLLOW_LINKS)) {
try {
return listFiles(dir);
} catch (IOException e) {
// the exception thrown when iterating a DirectoryStream if an I/O exception occurs
throw new DirectoryIteratorException(e);
}
return ImmutableList.of();
}
return ImmutableList.of();
}

/**
Expand Down

0 comments on commit af7514a

Please sign in to comment.