/* * Copyright 2013-2016 Cel Skeggs * * This file is part of the CCRE, the Common Chicken Runtime Engine. * * The CCRE is free software: you can redistribute it and/or modify it under the * terms of the GNU Lesser General Public License as published by the Free * Software Foundation, either version 3 of the License, or (at your option) any * later version. * * The CCRE is distributed in the hope that it will be useful, but WITHOUT ANY * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR * A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. * * You should have received a copy of the GNU Lesser General Public License * along with the CCRE. If not, see <http://www.gnu.org/licenses/>. */ package ccre.channel; import ccre.verifier.FlowPhase; import ccre.verifier.SetupPhase; /** * A BooleanInput is a way to get the current state of a boolean input, and to * subscribe to notifications of changes in the boolean input's value. * * A BooleanInput also acts as an UpdatingInput that updates when the value * changes, and never updates when the value doesn't change. * * @author skeggsc */ public interface BooleanInput extends UpdatingInput { /** * A BooleanInput that is always false. */ public static BooleanInput alwaysFalse = new BooleanInput() { @Override public boolean get() { return false; } @Override public CancelOutput onUpdate(EventOutput notify) { if (notify == null) { throw new NullPointerException(); } return CancelOutput.nothing; } }; /** * A BooleanInput that is always true. */ public static BooleanInput alwaysTrue = new BooleanInput() { @Override public boolean get() { return true; } @Override public CancelOutput onUpdate(EventOutput notify) { if (notify == null) { throw new NullPointerException(); } return CancelOutput.nothing; } }; /** * Returns a BooleanInput that is always the specified value, and never * changes. * * @see #alwaysFalse * @see #alwaysTrue * @param b the value that the input should always hold. * @return the BooleanInput. */ @SetupPhase public static BooleanInput always(boolean b) { return b ? alwaysTrue : alwaysFalse; } /** * Gets the current state of this boolean input. * * @return The current value. */ @FlowPhase public boolean get(); /** * Subscribes to changes in this boolean input's value. The boolean output * will be modified whenever the value of this input changes. The boolean * output will not be modified if the value of this input does not change. * * TODO: ensure that's true everywhere. * * If available, the current value of the input will be written at this * time. * * If the same listener is added multiple times, it has the same effect as * if it was added once. * * @param output The boolean output to notify when the value changes. * @return a CancelOutput that deregisters the registered EventOutput. DO * NOT FIRE THIS RETURNED EVENT MORE THAN ONCE: UNDEFINED BEHAVIOR MAY * RESULT. */ @SetupPhase public default CancelOutput send(BooleanOutput output) { output.safeSet(get()); return onUpdate(() -> output.set(get())); } /** * Provides an inverted version of this BooleanInput. * * This is defined as: * <ul> * <li>The get() method always returns the opposite of the original.</li> * <li>The onUpdate() and onUpdateR() methods always dispatch to the same * methods on the original.</li> * <li>Other operations may be overridden, but must have the same effective * result implied by the first two rules.</li> * </ul> * * @return the inverted version. */ @SetupPhase public default BooleanInput not() { BooleanInput original = this; return new BooleanInput() { @Override public CancelOutput onUpdate(EventOutput notify) { return original.onUpdate(notify); } @Override public boolean get() { return !original.get(); } @Override public BooleanInput not() { return original; } }; } /** * Provides a new BooleanInput that is true exactly when the two specified * BooleanInputs are true, and false otherwise. * * @param b the other BooleanInput. * @return the combined BooleanInput. */ @SetupPhase public default BooleanInput and(final BooleanInput b) { if (b == null) { throw new NullPointerException(); } final BooleanInput a = this; return new DerivedBooleanInput(a, b) { @Override protected boolean apply() { return a.get() && b.get(); } }; } /** * Provides a new BooleanInput that is true exactly when this BooleanInput * is true, and the specified BooleanInput is false, and false otherwise. * * @param b the other BooleanInput. * @return the combined BooleanInput. */ @SetupPhase public default BooleanInput andNot(final BooleanInput b) { return this.and(b.not()); } /** * Provides a new BooleanInput that is true exactly when the two specified * BooleanInputs have different values, and false otherwise. * * @param b the other BooleanInput. * @return the combined BooleanInput. */ @SetupPhase public default BooleanInput xor(final BooleanInput b) { if (b == null) { throw new NullPointerException(); } final BooleanInput a = this; return new DerivedBooleanInput(a, b) { @Override protected boolean apply() { return a.get() ^ b.get(); } }; } /** * Provides a new BooleanInput that is true exactly when either of the two * specified BooleanInputs is true, and false otherwise. * * @param b the other BooleanInput. * @return the combined BooleanInput. */ @SetupPhase public default BooleanInput or(final BooleanInput b) { if (b == null) { throw new NullPointerException(); } final BooleanInput a = this; return new DerivedBooleanInput(a, b) { @Override protected boolean apply() { return a.get() || b.get(); } }; } /** * Provides a new BooleanInput that is true exactly either this BooleanInput * is true, or the specified BooleanInput is false, and false otherwise. * * @param b the other BooleanInput. * @return the combined BooleanInput. */ @SetupPhase public default BooleanInput orNot(final BooleanInput b) { return this.or(b.not()); } /** * Provides an EventInput that is produced exactly once for each time that * the BooleanInput changes from false to true. * * @return the event that occurs when the value changes to true. */ @SetupPhase public default EventInput onPress() { return new DerivedEventInput(this) { @Override protected boolean shouldProduce() { return get(); } }; } /** * Fires an EventOutput exactly once for each time that the BooleanInput * changes from false to true. * * @param event the event to fire */ @SetupPhase public default void onPress(EventOutput event) { onPress().send(event); } /** * Provides an EventInput that is produced exactly once for each time that * the BooleanInput changes from true to false. * * @return the event that occurs when the value changes to false. */ @SetupPhase public default EventInput onRelease() { return new DerivedEventInput(this) { @Override protected boolean shouldProduce() { return !get(); } }; } /** * Fires an EventOutput exactly once for each time that the BooleanInput * changes from true to false. * * @param event the event to fire */ @SetupPhase public default void onRelease(EventOutput event) { onRelease().send(event); } /** * Provides an EventInput that is produced exactly once for each time that * the BooleanInput changes between true and false. * * @return the event that occurs when the value changes. */ @SetupPhase public default EventInput onChange() { return new DerivedEventInput(this) { @Override protected boolean shouldProduce() { return true; } }; } /** * Fires an EventOutput exactly once for each time that the BooleanInput * changes between true and false. * * @param event the event to fire */ @SetupPhase public default void onChange(EventOutput event) { onChange().send(event); } /** * Provides a FloatInput whose value is selected from <code>off</code> and * <code>on</code> based on the value of the BooleanInput. * * @param off the value used when the BooleanInput's value is false. * @param on the value used when the BooleanInput's value is true. * @return the FloatInput based on this BooleanInput. */ @SetupPhase public default FloatInput toFloat(final float off, final float on) { return new DerivedFloatInput(this) { @Override protected float apply() { return BooleanInput.this.get() ? on : off; } }; } /** * Provides a FloatInput whose value is selected from <code>off</code> and * <code>on</code> based on the value of the BooleanInput. * * @param off the value used when the BooleanInput's value is false. * @param on the input used when the BooleanInput's value is true. * @return the FloatInput based on this BooleanInput. */ @SetupPhase public default FloatInput toFloat(float off, FloatInput on) { return new DerivedFloatInput(this, on) { @Override protected float apply() { return BooleanInput.this.get() ? on.get() : off; } }; } /** * Provides a FloatInput whose value is selected from <code>off</code> and * <code>on</code> based on the value of the BooleanInput. * * @param off the input used when the BooleanInput's value is false. * @param on the value used when the BooleanInput's value is true. * @return the FloatInput based on this BooleanInput. */ @SetupPhase public default FloatInput toFloat(FloatInput off, float on) { return new DerivedFloatInput(this, off) { @Override protected float apply() { return BooleanInput.this.get() ? on : off.get(); } }; } /** * Provides a FloatInput whose value is selected from <code>off</code> and * <code>on</code> based on the value of the BooleanInput. * * @param off the input used when the BooleanInput's value is false. * @param on the input used when the BooleanInput's value is true. * @return the FloatInput based on this BooleanInput. */ @SetupPhase public default FloatInput toFloat(FloatInput off, FloatInput on) { return new DerivedFloatInput(this, off, on) { @Override protected float apply() { return BooleanInput.this.get() ? on.get() : off.get(); } }; } /** * Provides a BooleanInput whose value is selected from <code>off</code> and * <code>on</code> based on the value of this BooleanInput. * * <code>BooleanInput.alwaysTrue.select(off, on)</code> is equivalent to * <code>on</code> <code>BooleanInput.alwaysFalse.select(off, on)</code> is * equivalent to <code>off</code> * * @param off the input used when this BooleanInput's value is false. * @param on the input used when this BooleanInput's value is true. * @return the BooleanInput based on this BooleanInput and the two * arguments. */ @SetupPhase public default BooleanInput select(boolean off, boolean on) { return new DerivedBooleanInput(this) { @Override protected boolean apply() { return BooleanInput.this.get() ? on : off; } }; } /** * Provides a BooleanInput whose value is selected from <code>off</code> and * <code>on</code> based on the value of this BooleanInput. * * <code>BooleanInput.alwaysTrue.select(off, on)</code> is equivalent to * <code>on</code> <code>BooleanInput.alwaysFalse.select(off, on)</code> is * equivalent to <code>off</code> * * @param off the input used when this BooleanInput's value is false. * @param on the input used when this BooleanInput's value is true. * @return the BooleanInput based on this BooleanInput and the two * arguments. */ @SetupPhase public default BooleanInput select(boolean off, BooleanInput on) { return new DerivedBooleanInput(this, on) { @Override protected boolean apply() { return BooleanInput.this.get() ? on.get() : off; } }; } /** * Provides a BooleanInput whose value is selected from <code>off</code> and * <code>on</code> based on the value of this BooleanInput. * * <code>BooleanInput.alwaysTrue.select(off, on)</code> is equivalent to * <code>on</code> <code>BooleanInput.alwaysFalse.select(off, on)</code> is * equivalent to <code>off</code> * * @param off the input used when this BooleanInput's value is false. * @param on the input used when this BooleanInput's value is true. * @return the BooleanInput based on this BooleanInput and the two * arguments. */ @SetupPhase public default BooleanInput select(BooleanInput off, boolean on) { return new DerivedBooleanInput(this, off) { @Override protected boolean apply() { return BooleanInput.this.get() ? on : off.get(); } }; } /** * Provides a BooleanInput whose value is selected from <code>off</code> and * <code>on</code> based on the value of this BooleanInput. * * <code>BooleanInput.alwaysTrue.select(off, on)</code> is equivalent to * <code>on</code> <code>BooleanInput.alwaysFalse.select(off, on)</code> is * equivalent to <code>off</code> * * @param off the input used when this BooleanInput's value is false. * @param on the input used when this BooleanInput's value is true. * @return the BooleanInput based on this BooleanInput and the two * arguments. */ @SetupPhase public default BooleanInput select(BooleanInput off, BooleanInput on) { return new DerivedBooleanInput(this, off, on) { @Override protected boolean apply() { return BooleanInput.this.get() ? on.get() : off.get(); } }; } /** * Provides a version of this BooleanInput that only changes if * <code>allow</code> is currently true. * * When <code>allow</code> changes to false, the value is locked, and when * <code>allow</code> changes to true, the value is unlocked and set to the * current value of this BooleanInput. * * @param allow when to allow changing of the result. * @return the lockable version of this BooleanInput. */ @SetupPhase public default BooleanInput filterUpdates(BooleanInput allow) { final BooleanInput original = this; return new DerivedBooleanInput(this, allow) { private boolean lastValue = original.get(); @Override public boolean apply() { if (allow.get()) { lastValue = original.get(); } return lastValue; } }; } /** * Provides a version of this BooleanInput that only changes if * <code>deny</code> is currently false. * * When <code>deny</code> changes to true, the value is locked, and when * <code>deny</code> changes to false, the value is unlocked and set to the * current value of this BooleanInput. * * @param deny when to deny changing of the result. * @return the lockable version of this BooleanInput. */ @SetupPhase public default BooleanInput filterUpdatesNot(BooleanInput deny) { return this.filterUpdates(deny.not()); } }