    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      http://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017
    018package org.apache.commons.io.file;
    019
    020import java.math.BigInteger;
    021import java.util.Objects;
    022
    023/**
    024 * Provides counters for files, directories, and sizes, as a visit proceeds.
    025 *
    026 * @since 2.7
    027 */
    028public class Counters {
    029
    030    /**
    031     * Counts files, directories, and sizes, as a visit proceeds.
    032     */
    033    private static class AbstractPathCounters implements PathCounters {
    034
    035        private final Counter byteCounter;
    036        private final Counter directoryCounter;
    037        private final Counter fileCounter;
    038
    039        /**
    040         * Constructs a new instance.
    041         *
    042         * @param byteCounter the byte counter.
    043         * @param directoryCounter the directory counter.
    044         * @param fileCounter the file counter.
    045         */
    046        protected AbstractPathCounters(final Counter byteCounter, final Counter directoryCounter, final Counter fileCounter) {
    047            this.byteCounter = byteCounter;
    048            this.directoryCounter = directoryCounter;
    049            this.fileCounter = fileCounter;
    050        }
    051
    052        @Override
    053        public boolean equals(final Object obj) {
    054            if (this == obj) {
    055                return true;
    056            }
    057            if (!(obj instanceof AbstractPathCounters)) {
    058                return false;
    059            }
    060            final AbstractPathCounters other = (AbstractPathCounters) obj;
    061            return Objects.equals(byteCounter, other.byteCounter)
    062                && Objects.equals(directoryCounter, other.directoryCounter)
    063                && Objects.equals(fileCounter, other.fileCounter);
    064        }
    065
    066        @Override
    067        public Counter getByteCounter() {
    068            return byteCounter;
    069        }
    070
    071        @Override
    072        public Counter getDirectoryCounter() {
    073            return directoryCounter;
    074        }
    075
    076        /**
    077         * Gets the count of visited files.
    078         *
    079         * @return the byte count of visited files.
    080         */
    081        @Override
    082        public Counter getFileCounter() {
    083            return this.fileCounter;
    084        }
    085
    086        @Override
    087        public int hashCode() {
    088            return Objects.hash(byteCounter, directoryCounter, fileCounter);
    089        }
    090
    091        @Override
    092        public void reset() {
    093            byteCounter.reset();
    094            directoryCounter.reset();
    095            fileCounter.reset();
    096        }
    097
    098        @Override
    099        public String toString() {
    100            return String.format("%,d files, %,d directories, %,d bytes", Long.valueOf(fileCounter.get()),
    101                Long.valueOf(directoryCounter.get()), Long.valueOf(byteCounter.get()));
    102        }
    103
    104    }
    105
    106    /**
    107     * Counts using a {@link BigInteger} number.
    108     */
    109    private static final class BigIntegerCounter implements Counter {
    110
    111        private BigInteger value = BigInteger.ZERO;
    112
    113        @Override
    114        public void add(final long val) {
    115            value = value.add(BigInteger.valueOf(val));
    116
    117        }
    118
    119        @Override
    120        public boolean equals(final Object obj) {
    121            if (this == obj) {
    122                return true;
    123            }
    124            if (!(obj instanceof Counter)) {
    125                return false;
    126            }
    127            final Counter other = (Counter) obj;
    128            return Objects.equals(value, other.getBigInteger());
    129        }
    130
    131        @Override
    132        public long get() {
    133            return value.longValueExact();
    134        }
    135
    136        @Override
    137        public BigInteger getBigInteger() {
    138            return value;
    139        }
    140
    141        @Override
    142        public Long getLong() {
    143            return Long.valueOf(value.longValueExact());
    144        }
    145
    146        @Override
    147        public int hashCode() {
    148            return Objects.hash(value);
    149        }
    150
    151        @Override
    152        public void increment() {
    153            value = value.add(BigInteger.ONE);
    154        }
    155
    156        @Override
    157        public void reset() {
    158            value = BigInteger.ZERO;
    159        }
    160
    161        @Override
    162        public String toString() {
    163            return value.toString();
    164        }
    165    }
    166
    167    /**
    168     * Counts files, directories, and sizes, as a visit proceeds, using BigInteger numbers.
    169     */
    170    private final static class BigIntegerPathCounters extends AbstractPathCounters {
    171
    172        /**
    173         * Constructs a new initialized instance.
    174         */
    175        protected BigIntegerPathCounters() {
    176            super(Counters.bigIntegerCounter(), Counters.bigIntegerCounter(), Counters.bigIntegerCounter());
    177        }
    178
    179    }
    180
    181    /**
    182     * Counts using a number.
    183     */
    184    public interface Counter {
    185
    186        /**
    187         * Adds the given number to this counter.
    188         *
    189         * @param val the value to add.
    190         */
    191        void add(long val);
    192
    193        /**
    194         * Gets the counter as a long.
    195         *
    196         * @return the counter as a long.
    197         */
    198        long get();
    199
    200        /**
    201         * Gets the counter as a BigInteger.
    202         *
    203         * @return the counter as a BigInteger.
    204         */
    205        BigInteger getBigInteger();
    206
    207        /**
    208         * Gets the counter as a Long.
    209         *
    210         * @return the counter as a Long.
    211         */
    212        Long getLong();
    213
    214        /**
    215         * Adds one to this counter.
    216         */
    217        void increment();
    218
    219        /**
    220         * Resets this count to 0.
    221         */
    222        default void reset() {
    223            // binary compat, do nothing
    224        }
    225
    226    }
    227
    228    /**
    229     * Counts using a {@code long} number.
    230     */
    231    private final static class LongCounter implements Counter {
    232
    233        private long value;
    234
    235        @Override
    236        public void add(final long add) {
    237            value += add;
    238
    239        }
    240
    241        @Override
    242        public boolean equals(final Object obj) {
    243            if (this == obj) {
    244                return true;
    245            }
    246            if (!(obj instanceof Counter)) {
    247                return false;
    248            }
    249            final Counter other = (Counter) obj;
    250            return value == other.get();
    251        }
    252
    253        @Override
    254        public long get() {
    255            return value;
    256        }
    257
    258        @Override
    259        public BigInteger getBigInteger() {
    260            return BigInteger.valueOf(value);
    261        }
    262
    263        @Override
    264        public Long getLong() {
    265            return Long.valueOf(value);
    266        }
    267
    268        @Override
    269        public int hashCode() {
    270            return Objects.hash(value);
    271        }
    272
    273        @Override
    274        public void increment() {
    275            value++;
    276        }
    277
    278        @Override
    279        public void reset() {
    280            value = 0L;
    281        }
    282
    283        @Override
    284        public String toString() {
    285            return Long.toString(value);
    286        }
    287    }
    288
    289    /**
    290     * Counts files, directories, and sizes, as a visit proceeds, using long numbers.
    291     */
    292    private final static class LongPathCounters extends AbstractPathCounters {
    293
    294        /**
    295         * Constructs a new initialized instance.
    296         */
    297        protected LongPathCounters() {
    298            super(Counters.longCounter(), Counters.longCounter(), Counters.longCounter());
    299        }
    300
    301    }
    302
    303    /**
    304     * Counts nothing.
    305     */
    306    private final static class NoopCounter implements Counter {
    307
    308        static final NoopCounter INSTANCE = new NoopCounter();
    309
    310        @Override
    311        public void add(final long add) {
    312            // noop
    313        }
    314
    315        @Override
    316        public long get() {
    317            return 0;
    318        }
    319
    320        @Override
    321        public BigInteger getBigInteger() {
    322            return BigInteger.ZERO;
    323        }
    324
    325        @Override
    326        public Long getLong() {
    327            return 0L;
    328        }
    329
    330        @Override
    331        public void increment() {
    332            // noop
    333        }
    334
    335        /**
    336         * Returns {@code "0"}, always.
    337         *
    338         * @return {@code "0"}, always.
    339         * @since 2.12.0
    340         */
    341        @Override
    342        public String toString() {
    343            return "0";
    344        }
    345
    346    }
    347
    348    /**
    349     * Counts nothing.
    350     */
    351    private static final class NoopPathCounters extends AbstractPathCounters {
    352
    353        static final NoopPathCounters INSTANCE = new NoopPathCounters();
    354
    355        /**
    356         * Constructs a new initialized instance.
    357         */
    358        private NoopPathCounters() {
    359            super(Counters.noopCounter(), Counters.noopCounter(), Counters.noopCounter());
    360        }
    361
    362    }
    363
    364    /**
    365     * Counts files, directories, and sizes, as a visit proceeds.
    366     */
    367    public interface PathCounters {
    368
    369        /**
    370         * Gets the byte counter.
    371         *
    372         * @return the byte counter.
    373         */
    374        Counter getByteCounter();
    375
    376        /**
    377         * Gets the directory counter.
    378         *
    379         * @return the directory counter.
    380         */
    381        Counter getDirectoryCounter();
    382
    383        /**
    384         * Gets the file counter.
    385         *
    386         * @return the file counter.
    387         */
    388        Counter getFileCounter();
    389
    390        /**
    391         * Resets the counts to 0.
    392         */
    393        default void reset() {
    394            // binary compat, do nothing
    395        }
    396
    397    }
    398
    399    /**
    400     * Returns a new BigInteger Counter.
    401     *
    402     * @return a new BigInteger Counter.
    403     */
    404    public static Counter bigIntegerCounter() {
    405        return new BigIntegerCounter();
    406    }
    407
    408    /**
    409     * Returns a new BigInteger PathCounters.
    410     *
    411     * @return a new BigInteger PathCounters.
    412     */
    413    public static PathCounters bigIntegerPathCounters() {
    414        return new BigIntegerPathCounters();
    415    }
    416
    417    /**
    418     * Returns a new long Counter.
    419     *
    420     * @return a new long Counter.
    421     */
    422    public static Counter longCounter() {
    423        return new LongCounter();
    424    }
    425
    426    /**
    427     * Returns a new BigInteger PathCounters.
    428     *
    429     * @return a new BigInteger PathCounters.
    430     */
    431    public static PathCounters longPathCounters() {
    432        return new LongPathCounters();
    433    }
    434
    435    /**
    436     * Returns the no-op Counter.
    437     *
    438     * @return the no-op Counter.
    439     * @since 2.9.0
    440     */
    441    public static Counter noopCounter() {
    442        return NoopCounter.INSTANCE;
    443    }
    444
    445    /**
    446     * Returns the no-op PathCounters.
    447     *
    448     * @return the no-op PathCounters.
    449     * @since 2.9.0
    450     */
    451    public static PathCounters noopPathCounters() {
    452        return NoopPathCounters.INSTANCE;
    453    }
    454}