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(bigIntegerCounter(), bigIntegerCounter(), 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(longCounter(), longCounter(), 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(noopCounter(), noopCounter(), 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}