View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  
18  /*
19   * This is not the original file distributed by the Apache Software Foundation
20   * It has been modified by the Hipparchus project
21   */
22  package org.hipparchus.util;
23  
24  import org.hipparchus.exception.LocalizedCoreFormats;
25  import org.hipparchus.exception.MathIllegalArgumentException;
26  import org.hipparchus.exception.MathIllegalStateException;
27  import org.hipparchus.exception.NullArgumentException;
28  
29  /**
30   * Utility that increments a counter until a maximum is reached, at
31   * which point, the instance will by default throw a
32   * {@link MathIllegalStateException}.
33   * However, the user is able to override this behaviour by defining a
34   * custom {@link MaxCountExceededCallback callback}, in order to e.g.
35   * select which exception must be thrown.
36   */
37  public class Incrementor {
38      /** Default callback. */
39      private static final MaxCountExceededCallback DEFAULT_CALLBACK =
40          (int max) -> {
41              throw new MathIllegalStateException(LocalizedCoreFormats.MAX_COUNT_EXCEEDED, max);
42          };
43  
44      /** Upper limit for the counter. */
45      private final int maximalCount;
46      /** Function called at counter exhaustion. */
47      private final MaxCountExceededCallback maxCountCallback;
48      /** Current count. */
49      private int count;
50  
51      /**
52       * Defines a method to be called at counter exhaustion.
53       * The {@link #trigger(int) trigger} method should usually throw an exception.
54       */
55      public interface MaxCountExceededCallback {
56          /**
57           * Function called when the maximal count has been reached.
58           *
59           * @param maximalCount Maximal count.
60           * @throws MathIllegalStateException at counter exhaustion
61           */
62          void trigger(int maximalCount) throws MathIllegalStateException;
63      }
64  
65      /**
66       * Creates an Incrementor.
67       * <p>
68       * The maximal value will be set to {@code Integer.MAX_VALUE}.
69       */
70      public Incrementor() {
71          this(Integer.MAX_VALUE);
72      }
73  
74      /**
75       * Creates an Incrementor.
76       *
77       * @param max Maximal count.
78       * @throws MathIllegalArgumentException if {@code max} is negative.
79       */
80      public Incrementor(int max) {
81          this(max, DEFAULT_CALLBACK);
82      }
83  
84      /**
85       * Creates an Incrementor.
86       *
87       * @param max Maximal count.
88       * @param cb Function to be called when the maximal count has been reached.
89       * @throws NullArgumentException if {@code cb} is {@code null}.
90       * @throws MathIllegalArgumentException if {@code max} is negative.
91       */
92      public Incrementor(int max,
93                          MaxCountExceededCallback cb)
94          throws NullArgumentException {
95          this(0, max, cb);
96      }
97  
98      /**
99       * Creates an Incrementor.
100      *
101      * @param count Initial counter value.
102      * @param max Maximal count.
103      * @param cb Function to be called when the maximal count has been reached.
104      * @throws NullArgumentException if {@code cb} is {@code null}.
105      * @throws MathIllegalArgumentException if {@code max} is negative.
106      */
107     private Incrementor(int count,
108                         int max,
109                         MaxCountExceededCallback cb)
110         throws NullArgumentException {
111         if (cb == null) {
112             throw new NullArgumentException();
113         }
114         if (max < 0) {
115             throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_SMALL, max, 0);
116         }
117         this.maximalCount = max;
118         this.maxCountCallback = cb;
119         this.count = count;
120     }
121 
122     /**
123      * Creates a new instance and set the counter to the given value.
124      *
125      * @param value Value of the counter.
126      * @return a new instance.
127      */
128     public Incrementor withCount(int value) {
129         return new Incrementor(value,
130                                this.maximalCount,
131                                this.maxCountCallback);
132     }
133 
134     /**
135      * Creates a new instance with a given maximal count.
136      * The counter is reset to 0.
137      *
138      * @param max Maximal count.
139      * @return a new instance.
140      * @throws MathIllegalArgumentException if {@code max} is negative.
141      */
142     public Incrementor withMaximalCount(int max) {
143         return new Incrementor(0,
144                                max,
145                                this.maxCountCallback);
146     }
147 
148     /**
149      * Creates a new instance with a given callback.
150      * The counter is reset to 0.
151      *
152      * @param cb Callback to be called at counter exhaustion.
153      * @return a new instance.
154      */
155     public Incrementor withCallback(MaxCountExceededCallback cb) {
156         return new Incrementor(0,
157                                this.maximalCount,
158                                cb);
159     }
160 
161     /**
162      * Gets the upper limit of the counter.
163      *
164      * @return the counter upper limit.
165      */
166     public int getMaximalCount() {
167         return maximalCount;
168     }
169 
170     /**
171      * Gets the current count.
172      *
173      * @return the current count.
174      */
175     public int getCount() {
176         return count;
177     }
178 
179     /**
180      * Checks whether incrementing the counter {@code nTimes} is allowed.
181      *
182      * @return {@code false} if calling {@link #increment()}
183      * will trigger a {@code MathIllegalStateException},
184      * {@code true} otherwise.
185      */
186     public boolean canIncrement() {
187         return canIncrement(1);
188     }
189 
190     /**
191      * Checks whether incrementing the counter several times is allowed.
192      *
193      * @param nTimes Number of increments.
194      * @return {@code false} if calling {@link #increment(int)
195      * increment(nTimes)} would call the {@link MaxCountExceededCallback callback}
196      * {@code true} otherwise.
197      * @throws MathIllegalArgumentException if {@code nTimes} is negative.
198      */
199     public boolean canIncrement(int nTimes) {
200         if (nTimes < 0) {
201             throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_SMALL,
202                                                    nTimes, 0);
203         }
204         return count <= maximalCount - nTimes;
205     }
206 
207     /**
208      * Performs multiple increments.
209      *
210      * @param nTimes Number of increments.
211      * @throws MathIllegalArgumentException if {@code nTimes} is negative.
212      *
213      * @see #increment()
214      */
215     public void increment(int nTimes) {
216         if (nTimes < 0) {
217             throw new MathIllegalArgumentException(LocalizedCoreFormats.NUMBER_TOO_SMALL,
218                                                    nTimes, 0);
219         }
220 
221         for (int i = 0; i < nTimes; i++) {
222             increment();
223         }
224     }
225 
226     /**
227      * Adds the increment value to the current iteration count.
228      * At counter exhaustion, this method will call the
229      * {@link MaxCountExceededCallback#trigger(int) trigger} method of the
230      * callback object passed to the
231      * {@link #withCallback(MaxCountExceededCallback)} method.
232      *
233      * @see #increment(int)
234      */
235     public void increment() {
236         if (count > maximalCount - 1) {
237             maxCountCallback.trigger(maximalCount);
238         }
239         ++count;
240     }
241 
242     /**
243      * Resets the counter to 0.
244      */
245     public void reset() {
246         count = 0;
247     }
248 }