NodeAndDelegate.java

  1. /*
  2.  * *************************************************************************************************************************************************************
  3.  *
  4.  * SteelBlue: DCI User Interfaces
  5.  * http://tidalwave.it/projects/steelblue
  6.  *
  7.  * Copyright (C) 2015 - 2025 by Tidalwave s.a.s. (http://tidalwave.it)
  8.  *
  9.  * *************************************************************************************************************************************************************
  10.  *
  11.  * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License.
  12.  * You may obtain a copy of the License at
  13.  *
  14.  *     http://www.apache.org/licenses/LICENSE-2.0
  15.  *
  16.  * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
  17.  * CONDITIONS OF ANY KIND, either express or implied.  See the License for the specific language governing permissions and limitations under the License.
  18.  *
  19.  * *************************************************************************************************************************************************************
  20.  *
  21.  * git clone https://bitbucket.org/tidalwave/steelblue-src
  22.  * git clone https://github.com/tidalwave-it/steelblue-src
  23.  *
  24.  * *************************************************************************************************************************************************************
  25.  */
  26. package it.tidalwave.ui.javafx;

  27. import jakarta.annotation.Nonnull;
  28. import javafx.fxml.FXMLLoader;
  29. import javafx.scene.Node;
  30. import it.tidalwave.ui.javafx.impl.DefaultNodeAndDelegate;

  31. /***************************************************************************************************************************************************************
  32.  *
  33.  * This facility class create a thread-safe proxy for the JavaFX delegate (controller). Thread-safe means that it can
  34.  * be called by any thread and the JavaFX UI related stuff will be safely invoked in the JavaFX UI Thread.
  35.  * It is usually used in this way:
  36.  *
  37.  * <pre>
  38.  * // This is a Spring bean
  39.  * public class JavaFxFooBarPresentation implements FooBarPresentation
  40.  *   {
  41.  *     private static final String FXML_URL = "/my/package/javafx/FooBar.fxml";
  42.  *
  43.  *     {@literal @}Inject
  44.  *     private FlowController flowController;
  45.  *
  46.  *     private final NodeAndDelegate nad = createNodeAndDelegate(getClass(), FXML_URL);
  47.  *
  48.  *     private final FooBarPresentation delegate = nad.getDelegate();
  49.  *
  50.  *     public void showUp()
  51.  *       {
  52.  *         flowController.doSomething(nad.getNode());
  53.  *       }
  54.  *
  55.  *     public void showData (final String data)
  56.  *       {
  57.  *         delegate.showData(data);
  58.  *       }
  59.  *   }
  60.  * </pre>
  61.  *
  62.  * The method {@link #of(java.lang.Class, java.lang.String)} safely invokes the {@link FXMLLoader}
  63.  * and returns a {@link NodeAndDelegate} that contains both the visual {@link Node} and its delegate (controller).
  64.  *
  65.  * The latter is wrapped by a safe proxy that makes sure that any method invocation (such as {@code showData()} in the
  66.  * example is again executed in the JavaFX UI Thread. This means that the Presentation object methods can be invoked
  67.  * in any thread.
  68.  *
  69.  * For method returning {@code void}, the method invocation is asynchronous; that is, the caller is not blocked waiting
  70.  * for the method execution completion. If a return value is provided, the invocation is synchronous, and the caller
  71.  * will correctly wait the completion of the execution in order to get the result value.
  72.  *
  73.  * A typical JavaFX delegate (controller) looks like:
  74.  *
  75.  * <pre>
  76.  * // This is not a Spring bean - created by the FXMLLoader
  77.  * public class JavaFxFooBarPresentationDelegate implements FooBarPresentation
  78.  *   {
  79.  *     {@literal @}FXML
  80.  *     private Label label;
  81.  *
  82.  *     {@literal @}FXML
  83.  *     private Button button;
  84.  *
  85.  *     {@literal @}Inject // the only thing that can be injected, by means of JavaFXSafeProxyCreator
  86.  *     private JavaFxBinder binder;
  87.  *
  88.  *     {@literal @}Override
  89.  *     public void bind (final UserAction action)
  90.  *       {
  91.  *         binder.bind(button, action);
  92.  *       }
  93.  *
  94.  *     {@literal @}Override
  95.  *     public void showData (final String data)
  96.  *       {
  97.  *         label.setText(data);
  98.  *       }
  99.  *  }
  100.  * </pre>
  101.  *
  102.  * Not only all the methods invoked on the delegate are guaranteed to run in the JavaFX UI thread, but also its
  103.  * constructor, as per JavaFX requirements.
  104.  *
  105.  * A Presentation Delegate must not try to have dependency injection from Spring (for instance, by means of AOP),
  106.  * otherwise a deadlock could be triggered. Injection in constructors is safe.
  107.  *
  108.  * @author  Fabrizio Giudici
  109.  *
  110.  **************************************************************************************************************************************************************/
  111. public interface NodeAndDelegate<T>
  112.   {
  113.     /***********************************************************************************************************************************************************
  114.      * Creates a {@link NodeAndDelegate} for the given presentation class. The FXML resource name is inferred by
  115.      * default, For instance, is the class is named {@code JavaFXFooBarPresentation}, the resource name is
  116.      * {@code FooBar.fxml} and searched in the same packages as the class.
  117.      * @param     presentationClass   the class of the presentation for which the resources must be created.
  118.      * @since     1.0-ALPHA-13
  119.      * @see       #of(java.lang.Class, java.lang.String)
  120.      **********************************************************************************************************************************************************/
  121.     @Nonnull
  122.     public static <T> NodeAndDelegate<T> of (@Nonnull final Class<T> presentationClass)
  123.       {
  124.         return DefaultNodeAndDelegate.of(presentationClass);
  125.       }

  126.     /***********************************************************************************************************************************************************
  127.      * Creates a {@link NodeAndDelegate} for the given presentation class.
  128.      * @param   presentationClass   the class of the presentation for which the resources must be created.
  129.      * @param   fxmlResourcePath    the path of the FXML resource
  130.      **********************************************************************************************************************************************************/
  131.     @Nonnull
  132.     public static <T> NodeAndDelegate<T> of (@Nonnull final Class<T> presentationClass, @Nonnull final String fxmlResourcePath)
  133.       {
  134.         return DefaultNodeAndDelegate.of(presentationClass, fxmlResourcePath);
  135.       }

  136.     @Nonnull
  137.     public Node getNode();

  138.     @Nonnull
  139.     public T getDelegate();
  140.   }