// Path: src/main/java/alex/mojaki/boxes/PowerBox.java
// public interface PowerBox<T> extends Box<T> {
// 
//     /**
//      * Return the result of applying the {@link GetMiddleware}s added to the box's family, in the order they were
//      * added, to the value stored by this box, then similarly notify the {@link GetObserver}s.
//      */
//     @Override
//     T get();
// 
//     /**
//      * Change the value stored by this box to the result of applying the {@link ChangeMiddleware}s
//      * added to the box's family, in the order they were added, to the given parameter, then similarly notify
//      * the {@link ChangeObserver}s.
//      */
//     @Override
//     PowerBox<T> set(T value);
// 
//     /**
//      * Add {@link ChangeMiddleware}s to this box's family.
//      * Arguments equal to instances that have already been added will be ignored.
//      *
//      * @return this object for chaining
//      */
//     PowerBox<T> addChangeMiddleware(ChangeMiddleware... middlewares);
// 
//     /**
//      * Add {@link GetMiddleware}s to this box's family.
//      * Arguments equal to instances that have already been added will be ignored.
//      *
//      * @return this object for chaining
//      */
//     PowerBox<T> addGetMiddleware(GetMiddleware... middlewares);
// 
//     /**
//      * Add {@link ChangeObserver}s to this box's family.
//      * Arguments equal to instances that have already been added will be ignored.
//      *
//      * @return this object for chaining
//      */
//     PowerBox<T> addChangeObserver(ChangeObserver... observers);
// 
//     /**
//      * Add {@link GetObserver}s to this box's family.
//      * Arguments equal to instances that have already been added will be ignored.
//      *
//      * @return this object for chaining
//      */
//     PowerBox<T> addGetObserver(GetObserver... observers);
// 
//     /**
//      * Return the family that this box belongs to.
//      */
//     BoxFamily getFamily();
// }
// 
// Path: src/main/java/alex/mojaki/boxes/utils/InstanceStore.java
// public abstract class InstanceStore<T> {
// 
//     private final ConcurrentMap<List<Object>, T> map = new ConcurrentHashMap<List<Object>, T>();
// 
//     /**
//      * Override this method to return a new instance of the class of interest using the given arguments. This usually
//      * means calling a constructor.
//      */
//     public abstract T getNew(Object... args);
// 
//     /**
//      * Get an instance using the given arguments. If the arguments are new then a new instance
//      * will be returned via {@link InstanceStore#getNew(Object...)}. Otherwise a cached instance will be returned.
//      */
//     public T get(Object... args) {
//         List<Object> key = Arrays.asList(args);
//         T result = map.get(key);
//         if (result != null) {
//             return result;
//         }
//         synchronized (map) {
//             result = map.get(key);
//             if (result == null) {
//                 result = getNew(args);
//                 map.put(key, result);
//             }
//             return result;
//         }
//     }
// 
// }


import alex.mojaki.boxes.PowerBox;
import alex.mojaki.boxes.utils.InstanceStore;
import static alex.mojaki.boxes.utils.Utils.lessThan;
package alex.mojaki.boxes.middleware.symmetric;



// @formatter:off
/**
 * If the current value is beyond the given bounds, replace it with the bound it is above/below.
 * For example:
 *
 * <pre>{@code
 * PowerBox<Integer> percentage = new CommonBox<>(Example.class, "percentage")
 *         .addGetMiddleware(EnsureBounds.between(0, 100));
 * percentage.set(200);
 * name.get();  // returns 100
 * }</pre>
 *
 * You can specify two bounds with {@code between}, or a single bound with {@code minimum} or {@code maximum}.
 */
// @formatter:on
public class EnsureBounds<T> extends SymmetricMiddleware<Comparable<T>> {

    private static final InstanceStore<EnsureBounds> INSTANCE_STORE = new InstanceStore<EnsureBounds>() {
// Path: src/main/java/alex/mojaki/boxes/PowerBox.java
// public interface PowerBox<T> extends Box<T> {
// 
//     /**
//      * Return the result of applying the {@link GetMiddleware}s added to the box's family, in the order they were
//      * added, to the value stored by this box, then similarly notify the {@link GetObserver}s.
//      */
//     @Override
//     T get();
// 
//     /**
//      * Change the value stored by this box to the result of applying the {@link ChangeMiddleware}s
//      * added to the box's family, in the order they were added, to the given parameter, then similarly notify
//      * the {@link ChangeObserver}s.
//      */
//     @Override
//     PowerBox<T> set(T value);
// 
//     /**
//      * Add {@link ChangeMiddleware}s to this box's family.
//      * Arguments equal to instances that have already been added will be ignored.
//      *
//      * @return this object for chaining
//      */
//     PowerBox<T> addChangeMiddleware(ChangeMiddleware... middlewares);
// 
//     /**
//      * Add {@link GetMiddleware}s to this box's family.
//      * Arguments equal to instances that have already been added will be ignored.
//      *
//      * @return this object for chaining
//      */
//     PowerBox<T> addGetMiddleware(GetMiddleware... middlewares);
// 
//     /**
//      * Add {@link ChangeObserver}s to this box's family.
//      * Arguments equal to instances that have already been added will be ignored.
//      *
//      * @return this object for chaining
//      */
//     PowerBox<T> addChangeObserver(ChangeObserver... observers);
// 
//     /**
//      * Add {@link GetObserver}s to this box's family.
//      * Arguments equal to instances that have already been added will be ignored.
//      *
//      * @return this object for chaining
//      */
//     PowerBox<T> addGetObserver(GetObserver... observers);
// 
//     /**
//      * Return the family that this box belongs to.
//      */
//     BoxFamily getFamily();
// }
// 
// Path: src/main/java/alex/mojaki/boxes/utils/InstanceStore.java
// public abstract class InstanceStore<T> {
// 
//     private final ConcurrentMap<List<Object>, T> map = new ConcurrentHashMap<List<Object>, T>();
// 
//     /**
//      * Override this method to return a new instance of the class of interest using the given arguments. This usually
//      * means calling a constructor.
//      */
//     public abstract T getNew(Object... args);
// 
//     /**
//      * Get an instance using the given arguments. If the arguments are new then a new instance
//      * will be returned via {@link InstanceStore#getNew(Object...)}. Otherwise a cached instance will be returned.
//      */
//     public T get(Object... args) {
//         List<Object> key = Arrays.asList(args);
//         T result = map.get(key);
//         if (result != null) {
//             return result;
//         }
//         synchronized (map) {
//             result = map.get(key);
//             if (result == null) {
//                 result = getNew(args);
//                 map.put(key, result);
//             }
//             return result;
//         }
//     }
// 
// }


// Path: src/main/java/alex/mojaki/boxes/middleware/symmetric/EnsureBounds.java
import alex.mojaki.boxes.PowerBox;
import alex.mojaki.boxes.utils.InstanceStore;
import static alex.mojaki.boxes.utils.Utils.lessThan;

package alex.mojaki.boxes.middleware.symmetric;



// @formatter:off
/**
 * If the current value is beyond the given bounds, replace it with the bound it is above/below.
 * For example:
 *
 * <pre>{@code
 * PowerBox<Integer> percentage = new CommonBox<>(Example.class, "percentage")
 *         .addGetMiddleware(EnsureBounds.between(0, 100));
 * percentage.set(200);
 * name.get();  // returns 100
 * }</pre>
 *
 * You can specify two bounds with {@code between}, or a single bound with {@code minimum} or {@code maximum}.
 */
// @formatter:on
public class EnsureBounds<T> extends SymmetricMiddleware<Comparable<T>> {

    private static final InstanceStore<EnsureBounds> INSTANCE_STORE = new InstanceStore<EnsureBounds>() {
        @Override