com.facebook.buck.io.PathListing.java Source code

Java tutorial

Introduction

Here is the source code for com.facebook.buck.io.PathListing.java

Source

/*
 * Copyright 2014-present Facebook, Inc.
 *
 * 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.facebook.buck.io;

import com.google.common.base.Functions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.ImmutableSortedSet;
import com.google.common.collect.Ordering;

import java.io.IOException;
import java.nio.file.DirectoryStream;
import java.nio.file.Files;
import java.nio.file.NoSuchFileException;
import java.nio.file.Path;
import java.nio.file.attribute.FileTime;
import java.util.Optional;

/**
 * Utility class to list files which match a pattern, applying ordering
 * and filtering.
 */
public class PathListing {
    // Utility class, do not instantiate.
    private PathListing() {
    }

    /**
     * Whether to include files which match the filter, or exclude them.
     */
    public enum FilterMode {
        INCLUDE, EXCLUDE,
    }

    /**
     * Fetches last-modified time from a path.
     */
    public interface PathModifiedTimeFetcher {
        FileTime getLastModifiedTime(Path path) throws IOException;
    }

    /**
     * Uses {@code Files.getLastModifiedTime()} to get the last modified time
     * for a Path.
     */
    public static final PathModifiedTimeFetcher GET_PATH_MODIFIED_TIME = path -> Files.getLastModifiedTime(path);

    /**
     * Lists matching paths in descending modified time order.
     */
    public static ImmutableSortedSet<Path> listMatchingPaths(Path pathToGlob, String globPattern,
            PathModifiedTimeFetcher pathModifiedTimeFetcher) throws IOException {
        return listMatchingPathsWithFilters(pathToGlob, globPattern, pathModifiedTimeFetcher, FilterMode.INCLUDE,
                Optional.empty(), Optional.empty());
    }

    /**
     * Lists paths in descending modified time order,
     * excluding any paths which bring the number of files over {@code maxNumPaths}
     * or over {@code totalSizeFilter} bytes in size.
     */
    public static ImmutableSortedSet<Path> listMatchingPathsWithFilters(Path pathToGlob, String globPattern,
            PathModifiedTimeFetcher pathModifiedTimeFetcher, FilterMode filterMode,
            Optional<Integer> maxPathsFilter, Optional<Long> totalSizeFilter) throws IOException {

        // Fetch the modification time of each path and build a map of
        // (path => modification time) pairs.
        ImmutableMap.Builder<Path, FileTime> pathFileTimesBuilder = ImmutableMap.builder();
        try (DirectoryStream<Path> stream = Files.newDirectoryStream(pathToGlob, globPattern)) {
            for (Path path : stream) {
                try {
                    pathFileTimesBuilder.put(path, pathModifiedTimeFetcher.getLastModifiedTime(path));
                } catch (NoSuchFileException e) {
                    // Ignore the path.
                    continue;
                }
            }
        }
        ImmutableMap<Path, FileTime> pathFileTimes = pathFileTimesBuilder.build();

        ImmutableSortedSet<Path> paths = ImmutableSortedSet.copyOf(Ordering.natural()
                // Order the keys of the map (the paths) by their values (the file modification times).
                .onResultOf(Functions.forMap(pathFileTimes))
                // If two keys of the map have the same value, fall back to key order.
                .compound(Ordering.natural())
                // Use descending order.
                .reverse(), pathFileTimes.keySet());

        paths = applyNumPathsFilter(paths, filterMode, maxPathsFilter);
        paths = applyTotalSizeFilter(paths, filterMode, totalSizeFilter);
        return paths;
    }

    private static ImmutableSortedSet<Path> applyNumPathsFilter(ImmutableSortedSet<Path> paths,
            FilterMode filterMode, Optional<Integer> maxPathsFilter) {
        if (maxPathsFilter.isPresent()) {
            int limitIndex = Math.min(maxPathsFilter.get(), paths.size());
            paths = subSet(paths, filterMode, limitIndex);
        }
        return paths;
    }

    @SuppressWarnings("PMD.EmptyCatchBlock")
    private static ImmutableSortedSet<Path> applyTotalSizeFilter(ImmutableSortedSet<Path> paths,
            FilterMode filterMode, Optional<Long> totalSizeFilter) throws IOException {
        if (totalSizeFilter.isPresent()) {
            int limitIndex = 0;
            long totalSize = 0;
            for (Path path : paths) {
                try {
                    totalSize += Files.size(path);
                } catch (NoSuchFileException e) {
                    // Path was deleted; ignore it.
                }

                if (totalSize < totalSizeFilter.get()) {
                    limitIndex++;
                } else {
                    break;
                }
            }
            paths = subSet(paths, filterMode, limitIndex);
        }
        return paths;
    }

    private static ImmutableSortedSet<Path> subSet(ImmutableSortedSet<Path> paths, FilterMode filterMode,
            int limitIndex) {
        // This doesn't copy the contents of the ImmutableSortedSet. We use it
        // as a simple way to get O(1) access to the set's contents, as otherwise
        // we would have to iterate to find the Nth element.
        ImmutableList<Path> pathsList = paths.asList();
        boolean fullSet = limitIndex == paths.size();
        switch (filterMode) {
        case INCLUDE:
            // Make sure we don't call pathsList.get(pathsList.size()).
            if (!fullSet) {
                paths = paths.headSet(pathsList.get(limitIndex));
            }
            break;
        case EXCLUDE:
            if (fullSet) {
                // Make sure we don't call pathsList.get(pathsList.size()).
                paths = ImmutableSortedSet.of();
            } else {
                paths = paths.tailSet(pathsList.get(limitIndex));
            }
            break;
        }
        return paths;
    }
}