/* * Copyright 2011-2014 Eric F. Savage, code@efsavage.com * * 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.ajah.report; import java.io.File; import java.io.IOException; import java.text.NumberFormat; import java.util.logging.ConsoleHandler; import java.util.logging.Level; import lombok.extern.java.Log; import com.ajah.util.MathUtils; import com.ajah.util.data.DataSizeUnit; /** * A utility for scanning a directory and generating a report about it's * structure. * * @author <a href="http://efsavage.com">Eric F. Savage</a>, <a * href="mailto:code@efsavage.com">code@efsavage.com</a>. */ @Log public class FileScanReport { private long files; private long bytes; private long directories; private long emptyDirectories; private final File root; private final ReportWriter report; private static long[] ranges = new long[] { 0, 1, DataSizeUnit.KIBIBYTE.getBytes() - 1, DataSizeUnit.KIBIBYTE.getAsBytes(10), DataSizeUnit.KIBIBYTE.getAsBytes(100), DataSizeUnit.MEBIBYTE.getAsBytes(1), DataSizeUnit.MEBIBYTE.getAsBytes(10), DataSizeUnit.MEBIBYTE.getAsBytes(100), Long.MAX_VALUE }; private static long[][] rangeCountSizes = new long[ranges.length][2]; /** * Scans one or more directories. * * @param args * The directories to scan * @throws IOException * If a file or directory could not be scanned. */ public static void main(final String[] args) throws IOException { for (final String arg : args) { // Send logger output to our FileHandler. log.addHandler(new ConsoleHandler()); // Request that every detail gets logged. log.setLevel(Level.ALL); final File file = new File(arg); try (final ReportWriter report = new ReportWriter()) { report.add(System.out); report.set(log); report.add(new File("/tmp/report-" + System.currentTimeMillis() + ".txt")); report.rule(); report.println("Report for: " + file.getAbsolutePath()); final FileScanReport scan = new FileScanReport(file, report); scan.scan(); report.rule(); report.println("Scan Complete"); report.rule(); scan.report(0); } } } /** * Public constructor. * * @param root * The root directory to scan. * @param report * The report to write results to. */ public FileScanReport(final File root, final ReportWriter report) { this.root = root; this.report = report; } /** * Returns the number of bytes scanned. * * @return The number of bytes scanned. */ public long getBytes() { return this.bytes; } /** * Returns the number of directories scanned. * * @return The number of directories scanned. */ public long getDirectories() { return this.directories; } /** * Returns the number of empty directories scanned. * * @return The number of empty directories scanned. */ public long getEmptyDirectories() { return this.emptyDirectories; } /** * Returns the number of files scanned. * * @return The number of files scanned. */ public long getFiles() { return this.files; } /** * Returns the root file of the scan. * * @return The root file of the scan. */ public File getRoot() { return this.root; } private void report(final int depth) { this.report.println(depth, "Files: " + NumberFormat.getInstance().format(getFiles())); this.report.println(depth, "Directories: " + NumberFormat.getInstance().format(getDirectories())); this.report.println(depth, "Empty Directories: " + NumberFormat.getInstance().format(getEmptyDirectories())); this.report.println(depth, "Bytes: " + NumberFormat.getInstance().format(getBytes())); this.report.println(depth, "Size: " + DataSizeUnit.format(getBytes())); this.report.rule(); for (int range = 0; range < ranges.length - 1; range++) { this.report.println(depth, DataSizeUnit.format(ranges[range]) + " to " + DataSizeUnit.format(ranges[range + 1])); this.report.println(depth + 1, "Files: " + NumberFormat.getInstance().format(rangeCountSizes[range][0])); this.report.println(depth + 1, "Size: " + DataSizeUnit.format(rangeCountSizes[range][1])); } } /** * Executes a scan starting at the root directory. * * @throws IOException * If a file or directory could not be scanned. */ public void scan() throws IOException { final int depth = 0; scan(this.root, depth); } private long[] scan(final File file, final int depth) throws IOException { // log.info("Scanning: " + file.getAbsolutePath()); final long[] results = new long[4]; if (file.isDirectory()) { results[2]++; final File[] subFiles = file.listFiles(); if (subFiles == null || subFiles.length < 1) { results[3]++; } else { for (final File subFile : subFiles) { if (subFile.isDirectory()) { MathUtils.addTo(results, scan(subFile, depth + 1)); } else { results[0]++; // long oldSize = results[1]; final long fileSize = subFile.length(); results[1] += fileSize; // long newSize = results[1]; // Update the buckets for (int range = 0; range < ranges.length - 1; range++) { if (fileSize >= ranges[range] && fileSize < ranges[range + 1]) { // File Count rangeCountSizes[range][0]++; // File Size rangeCountSizes[range][1] += fileSize; } } } } } } else { results[0]++; results[1] += file.length(); } this.files = results[0]; this.bytes = results[1]; this.directories = results[2]; this.emptyDirectories = results[3]; // report(depth); return results; } }