View Javadoc
1   package org.opentrafficsim.base.parameters.constraint;
2   
3   import org.djunits.value.vdouble.scalar.base.DoubleScalar;
4   import org.djutils.exceptions.Throw;
5   
6   /**
7    * Continuous constraints with a single bound. To allow both {@code Double} and {@code DoubleScalar<?, ?>} constraints, the
8    * generic type is restricted to {@code Number}. However, that also allows other subclasses of {@code Number}, e.g.
9    * {@code Integer}. Due to rounding and value limits from the type (e.g. {@code Integer.MAX_VALEU}), bounds may not function
10   * correctly after a call to {@code Number.doubleValue()}. To restrict the usage, the constructor is private and static factory
11   * methods for {@code Double} and {@code DoubleScalar<?, ?>} are supplied.
12   * <p>
13   * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
14   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
15   * </p>
16   * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
17   * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
18   * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
19   * @param <T> value type
20   */
21  public class SingleBound<T extends Number> implements Constraint<T>
22  {
23  
24      /** The bound. */
25      private final Bound bound;
26  
27      /** Message about values that do not comply with the bound. */
28      private final String failMessage;
29  
30      /**
31       * Creates a lower inclusive bound; {@code value >= bound}.
32       * @param bound bound value
33       * @return lower inclusive bound
34       */
35      public static final SingleBound<Double> lowerInclusive(final double bound)
36      {
37          return createLowerInclusive(bound);
38      }
39  
40      /**
41       * Creates a lower inclusive bound; {@code value >= bound}.
42       * @param bound bound value
43       * @param <T> value type
44       * @return lower inclusive bound
45       */
46      public static final <T extends DoubleScalar<?, ?>> SingleBound<T> lowerInclusive(final T bound)
47      {
48          return createLowerInclusive(bound);
49      }
50  
51      /**
52       * Creates a lower inclusive bound; {@code value >= bound}.
53       * @param bound bound value
54       * @param <T> value type
55       * @return lower inclusive bound
56       */
57      private static <T extends Number> SingleBound<T> createLowerInclusive(final T bound)
58      {
59          return new SingleBound<>(new LowerBoundInclusive<>(bound),
60                  String.format("Value is not greater than or equal to %s", bound));
61      }
62  
63      /**
64       * Creates a lower exclusive bound; {@code value > bound}.
65       * @param bound bound value
66       * @return lower exclusive bound
67       */
68      public static final SingleBound<Double> lowerExclusive(final double bound)
69      {
70          return createLowerExclusive(bound);
71      }
72  
73      /**
74       * Creates a lower exclusive bound; {@code value > bound}.
75       * @param bound bound value
76       * @param <T> value type
77       * @return lower exclusive bound
78       */
79      public static final <T extends DoubleScalar<?, ?>> SingleBound<T> lowerExclusive(final T bound)
80      {
81          return createLowerExclusive(bound);
82      }
83  
84      /**
85       * Creates a lower exclusive bound; {@code value > bound}.
86       * @param bound bound value
87       * @param <T> value type
88       * @return lower exclusive bound
89       */
90      private static <T extends Number> SingleBound<T> createLowerExclusive(final T bound)
91      {
92          return new SingleBound<>(new LowerBoundExclusive<>(bound), String.format("Value is not greater than %s", bound));
93      }
94  
95      /**
96       * Creates an upper inclusive bound; {@code value <= bound}.
97       * @param bound bound value
98       * @return upper inclusive bound
99       */
100     public static final SingleBound<Double> upperInclusive(final double bound)
101     {
102         return createUpperInclusive(bound);
103     }
104 
105     /**
106      * Creates an upper inclusive bound; {@code value <= bound}.
107      * @param bound bound value
108      * @return upper inclusive bound
109      * @param <T> value type
110      */
111     public static final <T extends DoubleScalar<?, ?>> SingleBound<T> upperInclusive(final T bound)
112     {
113         return createUpperInclusive(bound);
114     }
115 
116     /**
117      * Creates an upper inclusive bound; {@code value <= bound}.
118      * @param bound bound value
119      * @param <T> value type
120      * @return upper inclusive bound
121      */
122     private static <T extends Number> SingleBound<T> createUpperInclusive(final T bound)
123     {
124         return new SingleBound<>(new UpperBoundInclusive<>(bound),
125                 String.format("Value is not smaller than or equal to %s", bound));
126     }
127 
128     /**
129      * Creates an upper exclusive bound; {@code value < bound}.
130      * @param bound bound value
131      * @return upper exclusive bound
132      */
133     public static final SingleBound<Double> upperExclusive(final double bound)
134     {
135         return createUpperExclusive(bound);
136     }
137 
138     /**
139      * Creates an upper exclusive bound; {@code value < bound}.
140      * @param bound bound value
141      * @param <T> value type
142      * @return upper exclusive bound
143      */
144     public static final <T extends DoubleScalar<?, ?>> SingleBound<T> upperExclusive(final T bound)
145     {
146         return createUpperExclusive(bound);
147     }
148 
149     /**
150      * Creates an upper exclusive bound; {@code value < bound}.
151      * @param bound bound value
152      * @param <T> value type
153      * @return upper exclusive bound
154      */
155     private static <T extends Number> SingleBound<T> createUpperExclusive(final T bound)
156     {
157         return new SingleBound<>(new UpperBoundExclusive<>(bound), String.format("Value is not smaller than %s", bound));
158     }
159 
160     /**
161      * Constructor.
162      * @param bound bound
163      * @param failMessage message about values that do not comply with the bound
164      */
165     SingleBound(final Bound bound, final String failMessage)
166     {
167         this.bound = bound;
168         this.failMessage = failMessage;
169     }
170 
171     @Override
172     public boolean accept(final T value)
173     {
174         return this.bound.accept(value);
175     }
176 
177     @Override
178     public String failMessage()
179     {
180         return this.failMessage;
181     }
182 
183     /**
184      * Returns the bound.
185      * @return bound.
186      */
187     public Bound getBound()
188     {
189         return this.bound;
190     }
191 
192     /**
193      * Super class for classes that implement a specific numeric check.
194      * <p>
195      * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
196      * <br>
197      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
198      * </p>
199      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
200      * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
201      * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
202      */
203     abstract static class Bound
204     {
205 
206         /** Value of the bound. */
207         @SuppressWarnings("checkstyle:visibilitymodifier")
208         final Number bound;
209 
210         /** Hashcode of the value class. */
211         @SuppressWarnings("checkstyle:visibilitymodifier")
212         final int classHashcode;
213 
214         /** String representation of this bound with %s for the value. */
215         private final String stringFormat;
216 
217         /**
218          * Constructor.
219          * @param bound value of the bound
220          * @param stringFormat string representation of this bound with %s for the value
221          */
222         Bound(final Number bound, final String stringFormat)
223         {
224             Throw.whenNull(bound, "Bound may not be null.");
225             Throw.whenNull(bound, "String format may not be null.");
226             Throw.when(Double.isNaN(bound.doubleValue()), IllegalArgumentException.class, "Bound value may not be NaN.");
227             this.bound = bound;
228             this.classHashcode = bound.getClass().hashCode();
229             this.stringFormat = stringFormat;
230         }
231 
232         /**
233          * Returns true if the bound accepts the value.
234          * @param value the value to check
235          * @return true if the bound accepts the value
236          */
237         abstract boolean accept(Number value);
238 
239         @Override
240         public final String toString()
241         {
242             return String.format(this.stringFormat, this.bound);
243         }
244     }
245 
246     /**
247      * Class implementing a lower inclusive bound; {@code value >= bound}.
248      * <p>
249      * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
250      * <br>
251      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
252      * </p>
253      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
254      * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
255      * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
256      * @param <T> value type
257      */
258     static class LowerBoundInclusive<T extends Number> extends Bound
259     {
260 
261         /**
262          * Constructor.
263          * @param bound bound
264          */
265         LowerBoundInclusive(final T bound)
266         {
267             super(bound, "%s <= value");
268         }
269 
270         @Override
271         protected boolean accept(final Number value)
272         {
273             return this.bound.doubleValue() <= value.doubleValue();
274         }
275 
276     }
277 
278     /**
279      * Class implementing a lower exclusive bound; {@code value > bound}.
280      * <p>
281      * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
282      * <br>
283      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
284      * </p>
285      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
286      * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
287      * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
288      * @param <T> value type
289      */
290     static class LowerBoundExclusive<T extends Number> extends Bound
291     {
292 
293         /**
294          * Constructor.
295          * @param bound bound
296          */
297         LowerBoundExclusive(final T bound)
298         {
299             super(bound, "%s < value");
300         }
301 
302         @Override
303         protected boolean accept(final Number value)
304         {
305             return this.bound.doubleValue() < value.doubleValue();
306         }
307 
308     }
309 
310     /**
311      * Class implementing an upper inclusive bound; {@code value <= bound}.
312      * <p>
313      * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
314      * <br>
315      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
316      * </p>
317      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
318      * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
319      * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
320      * @param <T> value type
321      */
322     static class UpperBoundInclusive<T extends Number> extends Bound
323     {
324 
325         /**
326          * Constructor.
327          * @param bound bound
328          */
329         UpperBoundInclusive(final T bound)
330         {
331             super(bound, "value <= %s");
332         }
333 
334         @Override
335         protected boolean accept(final Number value)
336         {
337             return this.bound.doubleValue() >= value.doubleValue();
338         }
339 
340     }
341 
342     /**
343      * Class implementing an upper exclusive bound; {@code value < bound}.
344      * <p>
345      * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
346      * <br>
347      * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
348      * </p>
349      * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
350      * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
351      * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
352      * @param <T> value type
353      */
354     static class UpperBoundExclusive<T extends Number> extends Bound
355     {
356 
357         /**
358          * Constructor.
359          * @param bound bound
360          */
361         UpperBoundExclusive(final T bound)
362         {
363             super(bound, "value < %s");
364         }
365 
366         @Override
367         protected boolean accept(final Number value)
368         {
369             return this.bound.doubleValue() > value.doubleValue();
370         }
371 
372     }
373 
374     @Override
375     public String toString()
376     {
377         return "SingleBound [" + this.bound + "]";
378     }
379 
380 }