OptionalUI.java

  1. package swingtree;

  2. import org.jspecify.annotations.NonNull;
  3. import org.jspecify.annotations.Nullable;
  4. import org.slf4j.Logger;
  5. import org.slf4j.LoggerFactory;

  6. import java.awt.Component;
  7. import java.util.NoSuchElementException;
  8. import java.util.Objects;
  9. import java.util.Optional;
  10. import java.util.function.Consumer;
  11. import java.util.function.Function;
  12. import java.util.function.Predicate;
  13. import java.util.function.Supplier;

  14. /**
  15.  * A container object for AWT {@link Component} types
  16.  * which may or may not contain a non-{@code null} value.
  17.  * If a value is present, {@code isPresent()} returns {@code true}. If no
  18.  * value is present, the object is considered <i>empty</i> and
  19.  * {@code isPresent()} returns {@code false}.
  20.  *
  21.  * <p>Additional methods that depend on the presence or absence of a contained
  22.  * value are provided, such as {@link #orElse(Component) orElse()}
  23.  * (returns a default value if no value is present) and
  24.  * {@link #ifPresent(Consumer) ifPresent()} (performs an
  25.  * action if a value is present).
  26.  *
  27.  * <p>This is a <b>value-based</b>
  28.  * class; use of identity-sensitive operations (including reference equality
  29.  * ({@code ==}), identity hash code, or synchronization) on instances of
  30.  * {@code OptionalUI} may have unpredictable results and should be avoided.
  31.  * <p>
  32.  * Note that
  33.  * {@code OptionalUI} is primarily intended for use as a SwingTree query return type where
  34.  * there is a clear need to represent "no result," and where returning {@code null} as well
  35.  * as expose the UI components to the application thread directly
  36.  * is likely to cause errors. A variable whose type is {@code OptionalUI} should
  37.  * never itself be {@code null}; it should always point to an {@code OptionalUI}
  38.  * instance.
  39.  *  <p>
  40.  *  <b>Please take a look at the <a href="https://globaltcad.github.io/swing-tree/">living swing-tree documentation</a>
  41.  *  where you can browse a large collection of examples demonstrating how to use the API of this class.</b>
  42.  *
  43.  * @param <C> the type of component held by this instance
  44.  */
  45. public final class OptionalUI<C extends Component> {

  46.     private static final Logger log = LoggerFactory.getLogger(OptionalUI.class);

  47.     /**
  48.      * Common instance for {@code empty()}.
  49.      */
  50.     private static final OptionalUI<?> EMPTY = new OptionalUI<>(null);

  51.     /**
  52.      * If non-null, the value; if null, indicates no value is present
  53.      */
  54.     private final @Nullable C _component;

  55.     /**
  56.      * Returns an empty {@code OptionalUI} instance.  No value is present for this
  57.      * {@code OptionalUI}.
  58.      *
  59.      * @apiNote
  60.      * Though it may be tempting to do so, avoid testing if an object is empty
  61.      * by comparing with {@code ==} against instances returned by
  62.      * {@code OptionalUI.empty()}.  There is no guarantee that it is a singleton.
  63.      * Instead, use {@link #isPresent()}.
  64.      *
  65.      * @param <T> The type of the non-existent value
  66.      * @return an empty {@code OptionalUI}
  67.      */
  68.     static<T extends Component> OptionalUI<T> empty() {
  69.         @SuppressWarnings("unchecked")
  70.         OptionalUI<T> t = (OptionalUI<T>) EMPTY;
  71.         return t;
  72.     }

  73.     /**
  74.      * Constructs an instance with the described value.
  75.      *
  76.      * @param value the value to describe; it's the caller's responsibility to
  77.      *        ensure the value is non-{@code null} unless creating the singleton
  78.      *        instance returned by {@code empty()}.
  79.      */
  80.     private OptionalUI( @Nullable C value ) { this._component = value; }

  81.     /**
  82.      * Returns an {@code OptionalUI} describing the given value, if
  83.      * non-{@code null}, otherwise returns an empty {@code OptionalUI}.
  84.      *
  85.      * @param component the possibly-{@code null} value to describe
  86.      * @param <T> the type of the value
  87.      * @return an {@code OptionalUI} with a present value if the specified value
  88.      *         is non-{@code null}, otherwise an empty {@code OptionalUI}
  89.      */
  90.     @SuppressWarnings("unchecked")
  91.     static <T extends Component> OptionalUI<T> ofNullable(@Nullable T component) {
  92.         return component == null ? (OptionalUI<T>) EMPTY
  93.                 : new OptionalUI<>(component);
  94.     }

  95.     /**
  96.      * If a component is present, returns {@code true}, otherwise {@code false}.
  97.      *
  98.      * @return {@code true} if a component is present, otherwise {@code false}
  99.      */
  100.     public boolean isPresent() { return _component != null; }

  101.     /**
  102.      * If a component is  not present, returns {@code true}, otherwise
  103.      * {@code false}.
  104.      *
  105.      * @return  {@code true} if a component is not present, otherwise {@code false}
  106.      */
  107.     public boolean isEmpty() { return _component == null; }

  108.     /**
  109.      * If a component is present, performs the given action with the component,
  110.      * otherwise does nothing.
  111.      *
  112.      * @param action the action to be performed, if a component is present
  113.      * @throws NullPointerException if component is present and the given action is
  114.      *         {@code null}
  115.      */
  116.     public void ifPresent( Consumer<? super C> action ) {
  117.         if ( _component != null )
  118.             UI.run(() -> action.accept(_component));
  119.     }

  120.     /**
  121.      * If a component is present, performs the given action with the component,
  122.      * otherwise performs the given empty-based action.
  123.      *
  124.      * @param action the action to be performed, if a component is present
  125.      * @param emptyAction the empty-based action to be performed, if no component is
  126.      *        present
  127.      * @throws NullPointerException if a component is present and the given action
  128.      *         is {@code null}, or no component is present and the given empty-based
  129.      *         action is {@code null}.
  130.      */
  131.     public void ifPresentOrElse( Consumer<? super C> action, Runnable emptyAction ) {
  132.         UI.run(()->{
  133.             try {
  134.                 if (_component != null)
  135.                     action.accept(_component);
  136.                 else
  137.                     emptyAction.run();
  138.             } catch (Exception e) {
  139.                 log.error("Error performing action on UI component of OptionalUI instance.", e);
  140.             }
  141.         });
  142.     }

  143.     /**
  144.      * If a component is present, and the component matches the given predicate,
  145.      * returns an {@code OptionalUI} describing the component, otherwise returns an
  146.      * empty {@code OptionalUI}.
  147.      *
  148.      * @param predicate the predicate to apply to a component, if present
  149.      * @return an {@code OptionalUI} describing the component of this
  150.      *         {@code OptionalUI}, if a component is present and the component matches the
  151.      *         given predicate, otherwise an empty {@code OptionalUI}
  152.      * @throws NullPointerException if the predicate is {@code null}
  153.      */
  154.     public OptionalUI<C> filter(Predicate<? super C> predicate) {
  155.         Objects.requireNonNull(predicate);
  156.         if (!isPresent()) {
  157.             return this;
  158.         } else {
  159.             try {
  160.                 if (!UI.thisIsUIThread()) {
  161.                     try {
  162.                         return UI.runAndGet(() -> filter(predicate));
  163.                     } catch (Exception e) {
  164.                         throw new RuntimeException(e);
  165.                     }
  166.                 } else
  167.                     return predicate.test(_component) ? this : empty();
  168.             } catch (Exception e) {
  169.                 log.error(
  170.                     "Error filtering UI component of OptionalUI instance. " +
  171.                     "Returning current OptionalUI instance instead.",
  172.                     e
  173.                 );
  174.             }
  175.         }
  176.         return this;
  177.     }

  178.     /**
  179.      * If a component is present, returns an {@code OptionalUI} describing (as if by
  180.      * {@link #ofNullable}) the result of applying the given mapping function to
  181.      * the component, otherwise returns an empty {@code OptionalUI}.
  182.      *
  183.      * <p>If the mapping function returns a {@code null} result then this method
  184.      * returns an empty {@code OptionalUI}.
  185.      *
  186.      * @param mapper the mapping function to apply to a component, if present
  187.      * @param <U> The type of the component returned from the mapping function
  188.      * @return an {@code Optional} describing the result of applying a mapping
  189.      *         function to the UI component of this {@code OptionalUI}, if a component is
  190.      *         present, otherwise an empty {@code OptionalUI}
  191.      * @throws NullPointerException if the mapping function is {@code null}
  192.      */
  193.     public <U> Optional<U> map( Function<? super C, ? extends U> mapper ) {
  194.         Objects.requireNonNull(mapper);
  195.         if ( !this.isPresent() ) return Optional.empty();
  196.         else {
  197.             try {
  198.                 if (UI.thisIsUIThread())
  199.                     return Optional.ofNullable(mapper.apply(_component));
  200.                 else {
  201.                     try {
  202.                         Optional<U> opt = UI.runAndGet(() -> map(mapper));
  203.                         if (opt.isPresent() && (opt.get() instanceof Component || opt.get() instanceof UIForAnySwing))
  204.                             throw new RuntimeException("A Swing component may not leak to another thread!");
  205.                         else return opt;
  206.                     } catch (Exception ex) {
  207.                         throw new RuntimeException(ex);
  208.                     }
  209.                 }
  210.             } catch (Exception ex) {
  211.                 log.error(
  212.                     "Error mapping OptionalUI to Optional instance! " +
  213.                     "Returning current OptionalUI instance instead.",
  214.                     ex
  215.                 );
  216.             }
  217.         }
  218.         return Optional.empty();
  219.     }

  220.     /**
  221.      *  An alternative to {@link #map(Function)} that maps to the same type in yet another
  222.      *  {@code OptionalUI} instance. This is useful for chaining UI centric operations.
  223.      *  The mapping function should return an {@code OptionalUI} instance.
  224.      *
  225.      * @param mapper The mapping function to apply to a component, if present.
  226.      * @return an {@code OptionalUI} describing the result of applying a mapping
  227.      *         function to the UI component of this {@code OptionalUI}, if a component is
  228.      *         present, otherwise an empty {@code OptionalUI}
  229.      * @throws NullPointerException if the mapping function is {@code null}
  230.      */
  231.     public OptionalUI<C> update( Function<C, C> mapper ) {
  232.         Objects.requireNonNull(mapper);
  233.         if ( !this.isPresent() ) return this;
  234.         else {
  235.             try {
  236.                 if (UI.thisIsUIThread())
  237.                     return OptionalUI.ofNullable(mapper.apply(_component));
  238.                 else {
  239.                     try {
  240.                         return UI.runAndGet(() -> update(mapper));
  241.                     } catch (Exception ex) {
  242.                         throw new RuntimeException(ex);
  243.                     }
  244.                 }
  245.             } catch (Exception ex) {
  246.                 log.error(
  247.                     "Error creating an updated OptionalUI instance! " +
  248.                     "Returning current OptionalUI instance instead.",
  249.                     ex
  250.                 );
  251.             }
  252.             return this;
  253.         }
  254.     }

  255.     /**
  256.      *  An alternative to {@link #update(Function)} and {@link #map(Function)} that maps to
  257.      *  the same type in yet another {@code OptionalUI} instance but with the
  258.      *  difference that the mapping function is only applied if the component is
  259.      *  present <b>and assignable to the given type</b>. <br>
  260.      *  It is a type conditional mapping operation.
  261.      *
  262.      * @param type The type to check if the component is assignable to.
  263.      * @param mapper The mapping function to apply to a component of the given type, if present.
  264.      * @return An {@code OptionalUI} describing the result of applying a mapping
  265.      *        function to the UI component of this {@code OptionalUI}, if a component is
  266.      *        present and the component is assignable to the given type, otherwise an
  267.      *        empty {@code OptionalUI}.
  268.      * @param <U> The type of the component returned from the mapping function.
  269.      * @throws NullPointerException if the mapping function is {@code null}
  270.      * @throws NullPointerException if the given type is {@code null}
  271.      */
  272.     public <U extends C> OptionalUI<C> updateIf(Class<U> type, Function<U, U> mapper) {
  273.         Objects.requireNonNull(type);
  274.         Objects.requireNonNull(mapper);
  275.         if ( !this.isPresent() ) return this;
  276.         else {
  277.             try {
  278.                 if (UI.thisIsUIThread()) {
  279.                     Objects.requireNonNull(_component);
  280.                     // Check if the component is assignable to the given type
  281.                     if (type.isAssignableFrom(_component.getClass())) {
  282.                         @SuppressWarnings("unchecked")
  283.                         U u = (U) _component;
  284.                         return OptionalUI.ofNullable(mapper.apply(u));
  285.                     } else {
  286.                         return this;
  287.                     }
  288.                 } else {
  289.                     try {
  290.                         return UI.runAndGet(() -> updateIf(type, mapper));
  291.                     } catch (Exception ex) {
  292.                         throw new RuntimeException(ex);
  293.                     }
  294.                 }
  295.             } catch (Exception ex) {
  296.                 log.error(
  297.                     "Error creating an updated OptionalUI instance! " +
  298.                     "Returning current OptionalUI instance instead.",
  299.                     ex
  300.                 );
  301.             }
  302.         }
  303.         return this;
  304.     }

  305.     /**
  306.      *  An alternative to {@link #update(Function)} and {@link #map(Function)} that maps to
  307.      *  the same type in yet another {@code OptionalUI} instance but with the
  308.      *  difference that the mapping function is only applied if the component is
  309.      *  present <b>and the supplied boolean is true</b>. <br>
  310.      *  It is a conditional mapping operation.
  311.      *
  312.      * @param condition The boolean to check before applying the mapping function.
  313.      * @param mapper The mapping function to apply to a component, if present and the condition is true.
  314.      * @return An {@code OptionalUI} describing the result of applying a mapping
  315.      *        function to the UI component of this {@code OptionalUI}, if a component is
  316.      *        present and the condition is true, otherwise an empty {@code OptionalUI}.
  317.      * @throws NullPointerException if the mapping function is {@code null}
  318.      */
  319.     public OptionalUI<C> updateIf( boolean condition, Function<C, C> mapper ) {
  320.         Objects.requireNonNull(mapper);
  321.         if ( !condition ) return this;
  322.         else
  323.             return update(mapper);
  324.     }

  325.     /**
  326.      * If a component is present, returns an {@code OptionalUI} describing the component,
  327.      * otherwise returns an {@code OptionalUI} produced by the supplying function.
  328.      * Use this to provide alternative UI components.
  329.      *
  330.      * @param supplier the supplying function that produces an {@code OptionalUI}
  331.      *        to be returned
  332.      * @return returns an {@code OptionalUI} describing the component of this
  333.      *         {@code OptionalUI}, if a component is present, otherwise an
  334.      *         {@code OptionalUI} produced by the supplying function.
  335.      * @throws NullPointerException if the supplying function is {@code null} or
  336.      *         produces a {@code null} result
  337.      */
  338.     public OptionalUI<C> or( Supplier<? extends OptionalUI<? extends C>> supplier ) {
  339.         Objects.requireNonNull(supplier);
  340.         if ( this.isPresent() ) return this;
  341.         else {
  342.             try {
  343.                 @SuppressWarnings("unchecked")
  344.                 OptionalUI<C> r = (OptionalUI<C>) supplier.get();
  345.                 return Objects.requireNonNull(r);
  346.             } catch (Exception e) {
  347.                 log.error(
  348.                     "Error creating fetching alternative OptionalUI instance! " +
  349.                     "Returning current OptionalUI instead.",
  350.                     e
  351.                 );
  352.             }
  353.         }
  354.         return this;
  355.     }

  356.     /**
  357.      * If no component is present, the supplying function is called to provide an
  358.      * alternative UI component to be used in place of the missing component.
  359.      * Otherwise, returns a {@code OptionalUI} containing the current component
  360.      * and the supplying function is not called.
  361.      * Use this to define alternative UI components.
  362.      *
  363.      * @param supplier the supplying function that produces a UI component to be
  364.      *                 used if no component is present.
  365.      * @return returns an {@code OptionalUI} describing the component of this
  366.      *         {@code OptionalUI}, if a component is present, otherwise an
  367.      *         {@code OptionalUI} produced by the supplying function.
  368.      * @throws NullPointerException if the supplying function is {@code null} or
  369.      *         produces a {@code null} result
  370.      */
  371.     public OptionalUI<C> orGet( Supplier<? extends C> supplier ) {
  372.         Objects.requireNonNull(supplier);
  373.         if ( this.isPresent() ) return this;
  374.         else {
  375.             try {
  376.                 C c = supplier.get();
  377.                 return OptionalUI.ofNullable(c);
  378.             } catch (Exception e) {
  379.                 log.error(
  380.                     "Error creating fetching alternative UI component! " +
  381.                     "Returning current OptionalUI instead.",
  382.                     e
  383.                 );
  384.             }
  385.         }
  386.         return this;
  387.     }

  388.     /**
  389.      * If no component is present and the supplied boolean is true,
  390.      * the supplying function is called to provide an
  391.      * alternative UI component to be used in place of the missing component.
  392.      * Otherwise, returns a {@code OptionalUI} containing the current component
  393.      * and the supplying function is not called.
  394.      * Use this to define alternative UI components if a condition is met.
  395.      *
  396.      * @param condition The boolean condition to check before calling the supplying function.
  397.      *                  If false, the supplying function is simply ignored.
  398.      * @param supplier the supplying function that produces a UI component to be
  399.      *                 used if no component is present.
  400.      * @return returns an {@code OptionalUI} describing the component of this
  401.      *         {@code OptionalUI}, if a component is present, otherwise an
  402.      *         {@code OptionalUI} produced by the supplying function.
  403.      * @throws NullPointerException if the supplying function is {@code null} or
  404.      *         produces a {@code null} result
  405.      */
  406.     public OptionalUI<C> orGetIf( boolean condition, Supplier<? extends C> supplier ) {
  407.         Objects.requireNonNull(supplier);
  408.         if ( !condition )
  409.             return this;
  410.         else
  411.             if ( this.isPresent() )
  412.                 return this;
  413.         else
  414.         {
  415.             try {
  416.                 C c = supplier.get();
  417.                 return OptionalUI.ofNullable(c);
  418.             } catch (Exception e) {
  419.                 log.error(
  420.                     "Error creating fetching alternative UI component! " +
  421.                     "Returning current OptionalUI instead.",
  422.                     e
  423.                 );
  424.             }
  425.         }
  426.         return this;
  427.     }

  428.     /**
  429.      * If a component is present, returns an {@code OptionalUI} describing the component,
  430.      * otherwise returns a {@code OptionalUI} containing the component built by the UI declaration
  431.      * inside the supplying function.
  432.      * Use this to provide alternative UI components.
  433.      *
  434.      * @param <A> The type of the component to be built.
  435.      * @param <B> The base type of the component to be built.
  436.      * @param supplier the supplying function that produces a UI declaration
  437.      *                 to be used if no component is present.
  438.      * @return returns an {@code OptionalUI} describing the component of this
  439.      *         {@code OptionalUI}, if a component is present, otherwise an
  440.      *         {@code OptionalUI} produced by the supplying function.
  441.      * @throws NullPointerException if the supplying function is {@code null} or
  442.      *         produces a {@code null} result
  443.      */
  444.     public <A extends B, B extends C> OptionalUI<C> orGetUi( Supplier<UIForAnything<?,A,B>> supplier ) {
  445.         Objects.requireNonNull(supplier);
  446.         if ( this.isPresent() ) return this;
  447.         else {
  448.             try {
  449.                 UIForAnything<?, A, B> ui = supplier.get();
  450.                 return OptionalUI.ofNullable(ui.get(ui.getType()));
  451.             } catch (Exception e) {
  452.                 log.error(
  453.                     "Error creating fetching alternative UI component! " +
  454.                     "Returning current OptionalUI instead.",
  455.                     e
  456.                 );
  457.             }
  458.         }
  459.         return this;
  460.     }

  461.     /**
  462.      * If no component is present and the supplied boolean is true,
  463.      * the supplying function is called to provide an
  464.      * alternative UI declaration to be used to build the missing component.
  465.      * Otherwise, returns the current {@code OptionalUI} and the supplying function is not called.
  466.      * Use this to define alternative UI declaration if a condition is met.
  467.      *
  468.      * @param <A> The type of the component to be built.
  469.      * @param <B> The base type of the component to be built.
  470.      * @param condition The boolean condition to check before calling the supplying function.
  471.      *                  If false, the supplying function is simply ignored.
  472.      * @param supplier the supplying function that produces a UI declaration
  473.      *                 to be used if no component is present.
  474.      * @return returns an {@code OptionalUI} describing the component of this
  475.      *         {@code OptionalUI}, if a component is present, otherwise an
  476.      *         {@code OptionalUI} produced by the supplying function.
  477.      * @throws NullPointerException if the supplying function is {@code null} or
  478.      *         produces a {@code null} result
  479.      */
  480.     public <A extends B, B extends C> OptionalUI<C> orGetUiIf( boolean condition, Supplier<UIForAnything<?,A,B>> supplier ) {
  481.         Objects.requireNonNull(supplier);
  482.         if ( !condition )
  483.             return this;
  484.         else
  485.             if ( this.isPresent() )
  486.                 return this;
  487.         else
  488.         {
  489.             try {
  490.                 UIForAnything<?, A, B> ui = supplier.get();
  491.                 return OptionalUI.ofNullable(ui.get(ui.getType()));
  492.             } catch (Exception e) {
  493.                 log.error(
  494.                     "Error creating fetching alternative UI component! " +
  495.                     "Returning current OptionalUI instead.",
  496.                     e
  497.                 );
  498.             }
  499.         }
  500.         return this;
  501.     }

  502.     /**
  503.      * If a component is present, returns the component, otherwise returns
  504.      * {@code other} or throws a null pointer exception if {@code other} is
  505.      * {@code null}.
  506.      *
  507.      * @param other the component to be returned, if no component is present.
  508.      *        May not be {@code null}.
  509.      * @return the component, if present, otherwise {@code other}
  510.      */
  511.     public @Nullable C orElseNullable( @Nullable C other ) {
  512.         if ( !UI.thisIsUIThread() )
  513.             throw new RuntimeException("The UI component may only be accessed by the UI thread!");
  514.         return _component != null ? _component : other;
  515.     }

  516.     /**
  517.      * If a component is present, returns the component, otherwise returns
  518.      * {@code other}.
  519.      *
  520.      * @param other the component to be returned, if no component is present.
  521.      *        May not be {@code null}, use {@link #orElseNullable(Component)} if it can be null.
  522.      * @return the component, if present, otherwise {@code other}
  523.      */
  524.     public C orElse( @NonNull C other ) {
  525.         if ( !UI.thisIsUIThread() )
  526.             throw new RuntimeException("The UI component may only be accessed by the UI thread!");
  527.         Objects.requireNonNull(other);
  528.         return _component != null ? _component : other;
  529.     }

  530.     /**
  531.      *  If a component is present, returns the component, otherwise returns {@code null}.
  532.      *
  533.      * @return The component wrapped in this OptionalUI, or null if no component is present.
  534.      */
  535.     public @Nullable C orNull() {
  536.         if ( !UI.thisIsUIThread() )
  537.             throw new RuntimeException("The UI component may only be accessed by the UI thread!");
  538.         return _component;
  539.     }

  540.     /**
  541.      * If a component is present, returns the component, otherwise returns the result
  542.      * produced by the supplying function.
  543.      *
  544.      * @param supplier the supplying function that produces a component to be returned
  545.      * @return the component, if present, otherwise the result produced by the
  546.      *         supplying function
  547.      * @throws NullPointerException if no component is present and the supplying
  548.      *         function is {@code null}
  549.      */
  550.     public C orElseGet(Supplier<? extends C> supplier) {
  551.         if ( !UI.thisIsUIThread() )
  552.             throw new RuntimeException("The UI component may only be accessed by the UI thread!");
  553.         return _component != null ? _component : supplier.get();
  554.     }

  555.     /**
  556.      * If a component is present, returns the component, otherwise throws
  557.      * {@code NoSuchElementException}.
  558.      *
  559.      * @return the non-{@code null} component described by this {@code OptionalUI}
  560.      * @throws NoSuchElementException if no component is present
  561.      */
  562.     public C orElseThrow() {
  563.         if ( !UI.thisIsUIThread() )
  564.             throw new RuntimeException("The UI component may only be accessed by the UI thread!");
  565.         if ( _component == null )
  566.             throw new NoSuchElementException("No component present");

  567.         return _component;
  568.     }

  569.     /**
  570.      * If a component is present, returns the component, otherwise throws an exception
  571.      * produced by the exception supplying function.
  572.      * <p>
  573.      * Note that
  574.      * A method reference to the exception constructor with an empty argument
  575.      * list can be used as the supplier. For example,
  576.      * {@code IllegalStateException::new}
  577.      *
  578.      * @param <X> Type of the exception to be thrown
  579.      * @param exceptionSupplier the supplying function that produces an
  580.      *        exception to be thrown
  581.      * @return the component, if present
  582.      * @throws X if no component is present
  583.      * @throws NullPointerException if no component is present and the exception
  584.      *          supplying function is {@code null}
  585.      */
  586.     public <X extends Throwable> C orElseThrow(Supplier<? extends X> exceptionSupplier) throws X {
  587.         if ( !UI.thisIsUIThread() )
  588.             throw new RuntimeException("The UI component may only be accessed by the UI thread!");
  589.         if (_component != null) {
  590.             return _component;
  591.         } else {
  592.             throw exceptionSupplier.get();
  593.         }
  594.     }

  595.     /**
  596.      * Indicates whether some other object is "equal to" this {@code OptionalUI}.
  597.      * The other object is considered equal if:
  598.      * <ul>
  599.      * <li>it is also an {@code OptionalUI} and;
  600.      * <li>both instances have no component present or;
  601.      * <li>the present components are "equal to" each other via {@code equals()}.
  602.      * </ul>
  603.      *
  604.      * @param obj an object to be tested for equality
  605.      * @return {@code true} if the other object is "equal to" this object
  606.      *         otherwise {@code false}
  607.      */
  608.     @Override
  609.     public boolean equals( Object obj ) {
  610.         if ( this == obj ) return true;
  611.         if ( !(obj instanceof OptionalUI) ) return false;
  612.         OptionalUI<?> other = (OptionalUI<?>) obj;
  613.         return Objects.equals(_component, other._component);
  614.     }

  615.     /**
  616.      * Returns the hash code of the component, if present, otherwise {@code 0}
  617.      * (zero) if no component is present.
  618.      *
  619.      * @return hash code component of the present component or {@code 0} if no component is
  620.      *         present
  621.      */
  622.     @Override
  623.     public int hashCode() { return Objects.hashCode(_component); }

  624.     /**
  625.      * Returns a non-empty string representation of this {@code OptionalUI}
  626.      * suitable for debugging.  The exact presentation format is unspecified and
  627.      * may vary between implementations and versions.
  628.      * <p>
  629.      * If a component is present the result must include its string representation
  630.      * in the result.  Empty and present {@code OptionalUI}s must be unambiguously
  631.      * differentiable.
  632.      *
  633.      * @return the string representation of this instance
  634.      */
  635.     @Override
  636.     public String toString() {
  637.         return _component != null
  638.                 ? String.format("OptionalUI[%s]", _component)
  639.                 : "OptionalUI.empty";
  640.     }

  641. }