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 }