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 java.io.IOException;
  29. import javafx.fxml.FXMLLoader;
  30. import javafx.scene.Node;
  31. import it.tidalwave.ui.javafx.impl.DefaultNodeAndDelegate;

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

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

  137.     @Nonnull
  138.     public static <T> NodeAndDelegate<T> load (@Nonnull final Class<T> clazz, @Nonnull final String resource)
  139.             throws IOException
  140.       {
  141.         return DefaultNodeAndDelegate.load(clazz, resource);
  142.       }

  143.     @Nonnull
  144.     public Node getNode();

  145.     @Nonnull
  146.     public T getDelegate();
  147.   }