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,
047            final Counter fileCounter) {
048            this.byteCounter = byteCounter;
049            this.directoryCounter = directoryCounter;
050            this.fileCounter = fileCounter;
051        }
052
053        @Override
054        public boolean equals(final Object obj) {
055            if (this == obj) {
056                return true;
057            }
058            if (!(obj instanceof AbstractPathCounters)) {
059                return false;
060            }
061            final AbstractPathCounters other = (AbstractPathCounters) obj;
062            return Objects.equals(byteCounter, other.byteCounter)
063                && Objects.equals(directoryCounter, other.directoryCounter)
064                && Objects.equals(fileCounter, other.fileCounter);
065        }
066
067        @Override
068        public Counter getByteCounter() {
069            return byteCounter;
070        }
071
072        @Override
073        public Counter getDirectoryCounter() {
074            return directoryCounter;
075        }
076
077        /**
078         * Gets the count of visited files.
079         *
080         * @return the byte count of visited files.
081         */
082        @Override
083        public Counter getFileCounter() {
084            return this.fileCounter;
085        }
086
087        @Override
088        public int hashCode() {
089            return Objects.hash(byteCounter, directoryCounter, fileCounter);
090        }
091
092        @Override
093        public void reset() {
094            byteCounter.reset();
095            directoryCounter.reset();
096            fileCounter.reset();
097        }
098
099        @Override
100        public String toString() {
101            return String.format("%,d files, %,d directories, %,d bytes", Long.valueOf(fileCounter.get()),
102                Long.valueOf(directoryCounter.get()), Long.valueOf(byteCounter.get()));
103        }
104
105    }
106
107    /**
108     * Counts using a BigInteger number.
109     */
110    private static final class BigIntegerCounter implements Counter {
111
112        private BigInteger value = BigInteger.ZERO;
113
114        @Override
115        public void add(final long val) {
116            value = value.add(BigInteger.valueOf(val));
117
118        }
119
120        @Override
121        public boolean equals(final Object obj) {
122            if (this == obj) {
123                return true;
124            }
125            if (!(obj instanceof Counter)) {
126                return false;
127            }
128            final Counter other = (Counter) obj;
129            return Objects.equals(value, other.getBigInteger());
130        }
131
132        @Override
133        public long get() {
134            return value.longValueExact();
135        }
136
137        @Override
138        public BigInteger getBigInteger() {
139            return value;
140        }
141
142        @Override
143        public Long getLong() {
144            return Long.valueOf(value.longValueExact());
145        }
146
147        @Override
148        public int hashCode() {
149            return Objects.hash(value);
150        }
151
152        @Override
153        public void increment() {
154            value = value.add(BigInteger.ONE);
155        }
156
157        @Override
158        public String toString() {
159            return value.toString();
160        }
161
162        @Override
163        public void reset() {
164            value = BigInteger.ZERO;
165        }
166    }
167
168    /**
169     * Counts files, directories, and sizes, as a visit proceeds, using BigInteger numbers.
170     */
171    private final static class BigIntegerPathCounters extends AbstractPathCounters {
172
173        /**
174         * Constructs a new initialized instance.
175         */
176        protected BigIntegerPathCounters() {
177            super(Counters.bigIntegerCounter(), Counters.bigIntegerCounter(), Counters.bigIntegerCounter());
178        }
179
180    }
181
182    /**
183     * Counts using a number.
184     */
185    public interface Counter {
186
187        /**
188         * Adds the given number to this counter.
189         *
190         * @param val the value to add.
191         */
192        void add(long val);
193
194        /**
195         * Gets the counter as a long.
196         *
197         * @return the counter as a long.
198         */
199        long get();
200
201        /**
202         * Gets the counter as a BigInteger.
203         *
204         * @return the counter as a BigInteger.
205         */
206        BigInteger getBigInteger();
207
208        /**
209         * Gets the counter as a Long.
210         *
211         * @return the counter as a Long.
212         */
213        Long getLong();
214
215        /**
216         * Adds one to this counter.
217         */
218        void increment();
219
220        /**
221         * Resets this count to 0.
222         */
223        default void reset() {
224            // binary compat, do nothing
225        }
226
227    }
228
229    /**
230     * Counts using a long number.
231     */
232    private final static class LongCounter implements Counter {
233
234        private long value;
235
236        @Override
237        public void add(final long add) {
238            value += add;
239
240        }
241
242        @Override
243        public boolean equals(final Object obj) {
244            if (this == obj) {
245                return true;
246            }
247            if (!(obj instanceof Counter)) {
248                return false;
249            }
250            final Counter other = (Counter) obj;
251            return value == other.get();
252        }
253
254        @Override
255        public long get() {
256            return value;
257        }
258
259        @Override
260        public BigInteger getBigInteger() {
261            return BigInteger.valueOf(value);
262        }
263
264        @Override
265        public Long getLong() {
266            return Long.valueOf(value);
267        }
268
269        @Override
270        public int hashCode() {
271            return Objects.hash(value);
272        }
273
274        @Override
275        public void increment() {
276            value++;
277        }
278
279        @Override
280        public String toString() {
281            return Long.toString(value);
282        }
283
284        @Override
285        public void reset() {
286            value = 0L;
287        }
288    }
289
290    /**
291     * Counts files, directories, and sizes, as a visit proceeds, using long numbers.
292     */
293    private final static class LongPathCounters extends AbstractPathCounters {
294
295        /**
296         * Constructs a new initialized instance.
297         */
298        protected LongPathCounters() {
299            super(Counters.longCounter(), Counters.longCounter(), Counters.longCounter());
300        }
301
302    }
303
304    /**
305     * Counts nothing.
306     */
307    private final static class NoopCounter implements Counter {
308
309        static final NoopCounter INSTANCE = new NoopCounter();
310
311        @Override
312        public void add(final long add) {
313            // noop
314        }
315
316        @Override
317        public long get() {
318            return 0;
319        }
320
321        @Override
322        public BigInteger getBigInteger() {
323            return BigInteger.ZERO;
324        }
325
326        @Override
327        public Long getLong() {
328            return 0L;
329        }
330
331        @Override
332        public void increment() {
333            // noop
334        }
335
336    }
337
338    /**
339     * Counts nothing.
340     */
341    private static final class NoopPathCounters extends AbstractPathCounters {
342
343        static final NoopPathCounters INSTANCE = new NoopPathCounters();
344
345        /**
346         * Constructs a new initialized instance.
347         */
348        private NoopPathCounters() {
349            super(Counters.noopCounter(), Counters.noopCounter(), Counters.noopCounter());
350        }
351
352    }
353
354    /**
355     * Counts files, directories, and sizes, as a visit proceeds.
356     */
357    public interface PathCounters {
358
359        /**
360         * Gets the byte counter.
361         *
362         * @return the byte counter.
363         */
364        Counter getByteCounter();
365
366        /**
367         * Gets the directory counter.
368         *
369         * @return the directory counter.
370         */
371        Counter getDirectoryCounter();
372
373        /**
374         * Gets the file counter.
375         *
376         * @return the file counter.
377         */
378        Counter getFileCounter();
379
380        /**
381         * Resets the counts to 0.
382         */
383        default void reset() {
384            // binary compat, do nothing
385        }
386
387    }
388
389    /**
390     * Returns a new BigInteger Counter.
391     *
392     * @return a new BigInteger Counter.
393     */
394    public static Counter bigIntegerCounter() {
395        return new BigIntegerCounter();
396    }
397
398    /**
399     * Returns a new BigInteger PathCounters.
400     *
401     * @return a new BigInteger PathCounters.
402     */
403    public static PathCounters bigIntegerPathCounters() {
404        return new BigIntegerPathCounters();
405    }
406
407    /**
408     * Returns a new long Counter.
409     *
410     * @return a new long Counter.
411     */
412    public static Counter longCounter() {
413        return new LongCounter();
414    }
415
416    /**
417     * Returns a new BigInteger PathCounters.
418     *
419     * @return a new BigInteger PathCounters.
420     */
421    public static PathCounters longPathCounters() {
422        return new LongPathCounters();
423    }
424
425    /**
426     * Returns the NOOP Counter.
427     *
428     * @return the NOOP Counter.
429     * @since 2.9.0
430     */
431    public static Counter noopCounter() {
432        return NoopCounter.INSTANCE;
433    }
434
435    /**
436     * Returns the NOOP PathCounters.
437     *
438     * @return the NOOP PathCounters.
439     * @since 2.9.0
440     */
441    public static PathCounters noopPathCounters() {
442        return NoopPathCounters.INSTANCE;
443    }
444}