CellConf.java

  1. package swingtree;

  2. import org.jspecify.annotations.Nullable;
  3. import org.slf4j.Logger;
  4. import org.slf4j.LoggerFactory;
  5. import swingtree.api.Configurator;
  6. import swingtree.layout.Size;

  7. import javax.swing.*;
  8. import java.awt.*;
  9. import java.util.List;
  10. import java.util.*;
  11. import java.util.function.Consumer;
  12. import java.util.function.Function;
  13. import java.util.function.Supplier;

  14. /**
  15.  * This class models the state of an individual table/tree/list/drop down cell alongside
  16.  * various properties that a cell should have, like for example
  17.  * the value of the cell, its position within the component
  18.  * as well as a {@link CellConf#view()} (renderer/editor) in the form of an AWT {@link Component}
  19.  * which may or may not be replaced or modified.
  20.  * <br>
  21.  * The {@link CellConf} is exposed to the {@link RenderAs#as(Configurator)}
  22.  * method after a {@link CellBuilder#when(Class)} call as part of various
  23.  * cell builder APIs like: <br>
  24.  * <ul>
  25.  *     <li>{@link UIForTable#withCell(Configurator)}</li>
  26.  *     <li>{@link UIForTable#withCells(Configurator)}</li>
  27.  *     <li>{@link UIForTable#withCellForColumn(String, Configurator)} </li>
  28.  *     <li>{@link UIForTable#withCellsForColumn(String, Configurator)} </li>
  29.  *     <li>{@link UIForTable#withCellForColumn(int, Configurator)} </li>
  30.  *     <li>{@link UIForTable#withCellsForColumn(int, Configurator)} </li>
  31.  *     <li>{@link UIForList#withCell(Configurator)} </li>
  32.  *     <li>{@link UIForList#withCells(Configurator)} </li>
  33.  * </ul>
  34.  * When configuring your cell, you may use methods like
  35.  * {@link CellConf#view(Component)} or {@link CellConf#renderer(Size,Consumer)}
  36.  * to define how the cell should be rendered.
  37.  * <p>
  38.  * Note that the {@link CellConf#isEditing()} flag determines
  39.  * two important modes in which this class is exposed to {@link RenderAs#as(Configurator)}.
  40.  * If the {@code isEditing()} is true, then you are expected to configure a
  41.  * cell editor component for the {@link CellConf#view()} property.
  42.  * If the {@code isEditing()} is false, then you are expected to configure a simple
  43.  * cell renderer component as the {@link CellConf#view()} property.<br>
  44.  * Note that for each state of the {@code isEditing()} flag, the view component
  45.  * is persisted across multiple calls to the {@link RenderAs#as(Configurator)} method.
  46.  * <p>
  47.  * This design allows you to easily define and continuously update both a
  48.  * renderer and an editor for a cell on a single call to the {@link RenderAs#as(Configurator)} method, and then
  49.  * to update the renderer or editor in every subsequent call to the same method.
  50.  *
  51.  * @param <V> The entry type of the entry of this {@link CellConf}.
  52.  */
  53. public final class CellConf<C extends JComponent, V>
  54. {
  55.     private static final Logger log = LoggerFactory.getLogger(CellConf.class);

  56.     static <C extends JComponent, V> CellConf<C, V> of(
  57.         @Nullable JList     jListIfInvolved,
  58.         @Nullable Component lastRenderer,
  59.         C                   owner,
  60.         @Nullable V         entry,
  61.         boolean             isSelected,
  62.         boolean             hasFocus,
  63.         boolean             isEditing,
  64.         boolean             isExpanded,
  65.         boolean             isLeaf,
  66.         int                 row,
  67.         int                 column,
  68.         Supplier<Component> defaultRenderSource
  69.     ) {
  70.         List<String> toolTips = new ArrayList<>();
  71.         return new CellConf<>(
  72.             jListIfInvolved,
  73.             owner,
  74.             entry,
  75.             isSelected,
  76.             hasFocus,
  77.             isEditing,
  78.             isExpanded,
  79.             isLeaf,
  80.             row,
  81.             column,
  82.             lastRenderer,
  83.             toolTips,
  84.             null,
  85.             defaultRenderSource
  86.         );
  87.     }

  88.     private final @Nullable JList<?>  jListIfInvolved;
  89.     private final C                   parent;
  90.     private final @Nullable V         entry;
  91.     private final boolean             isSelected;
  92.     private final boolean             hasFocus;
  93.     private final boolean             isEditing;
  94.     private final boolean             isExpanded;
  95.     private final boolean             isLeaf;
  96.     private final int                 row;
  97.     private final int                 column;
  98.     private final @Nullable Component view;
  99.     private final List<String>        toolTips;
  100.     private final @Nullable Object    presentationEntry;
  101.     private final Supplier<Component> defaultRenderSource;


  102.     private CellConf(
  103.             @Nullable JList     jListIfInvolved,
  104.             C                   host,
  105.             @Nullable V         entry,
  106.             boolean             isSelected,
  107.             boolean             hasFocus,
  108.             boolean             isEditing,
  109.             boolean             isExpanded,
  110.             boolean             isLeaf,
  111.             int                 row,
  112.             int                 column,
  113.             @Nullable Component view,
  114.             List<String>        toolTips,
  115.             @Nullable Object    presentationEntry,
  116.             Supplier<Component> defaultRenderSource
  117.     ) {
  118.         this.jListIfInvolved     = jListIfInvolved;
  119.         this.parent              = Objects.requireNonNull(host);
  120.         this.entry               = entry;
  121.         this.isSelected          = isSelected;
  122.         this.hasFocus            = hasFocus;
  123.         this.isEditing           = isEditing;
  124.         this.isExpanded          = isExpanded;
  125.         this.isLeaf              = isLeaf;
  126.         this.row                 = row;
  127.         this.column              = column;
  128.         this.view                = view;
  129.         this.toolTips            = Objects.requireNonNull(toolTips);
  130.         this.presentationEntry = presentationEntry;
  131.         this.defaultRenderSource = Objects.requireNonNull(defaultRenderSource);
  132.     }

  133.     /**
  134.      *  Returns the parent/host of this cell, i.e. the component
  135.      *  which contains this cell,
  136.      *  like a {@link JComboBox}, {@link JTable} or {@link JList}
  137.      *
  138.      * @return The owner of this cell, typically a table, list or combo box.
  139.      */
  140.     public C getHost() {
  141.         return parent;
  142.     }

  143.     /**
  144.      *  Some host components (see {@link #getHost()}, use a
  145.      *  {@link JList} in their look and feel to render cells.
  146.      *  This is the case for the {@link JComboBox} component, which
  147.      *  has a drop-down popup that is rendered through an internal
  148.      *  {@link JList} which you can access through this method.<br>
  149.      *  <p>
  150.      *      But note that this {@link JList} is returned through an {@link Optional}
  151.      *      because it may not exist for other host components like a {@link JTable} or {@link JTree}.
  152.      *      In case of this cell being directly used for a {@link JList}, through
  153.      *      {@link UIForList#withCell(Configurator)} or {@link UIForList#withCells(Configurator)},
  154.      *      then both {@link #getHost()} and this return the same {@link JList} instance.
  155.      *  </p>
  156.      *
  157.      * @return An optional containing a {@link JList} used for rendering this cell
  158.      *          if this is called by a {@link ListCellRenderer}, and an {@link Optional#empty()} otherwise.
  159.      */
  160.     public Optional<JList<?>> getListView() {
  161.         if ( parent instanceof JList )
  162.             return Optional.of((JList<?>)parent);
  163.         else
  164.             return Optional.ofNullable(jListIfInvolved);
  165.     }

  166.     /**
  167.      *  Returns the entry of this cell, which is the data
  168.      *  that this cell represents. The entry is wrapped in an
  169.      *  {@link Optional} to indicate that the entry may be null.
  170.      *  A cell entry is typically a string, number or custom user object.
  171.      *
  172.      * @return An optional of the entry of this cell, or an empty optional if the entry is null.
  173.      */
  174.     public Optional<V> entry() {
  175.         return Optional.ofNullable(entry);
  176.     }

  177.     /**
  178.      *  Returns the entry of this cell as a string. If the entry
  179.      *  is null, then an empty string is returned. This method is
  180.      *  useful when you want to display the entry of the cell as a string,
  181.      *  and do not have a special meaning assigned to null entries.
  182.      *  (Which is the preferred way to handle null entries)
  183.      *
  184.      * @return The entry of this cell as a string, or an empty string if the entry is null.
  185.      *        Note that the string representation of the entry is obtained by calling
  186.      *        the {@link Object#toString()} method on the entry.
  187.      */
  188.     public String entryAsString() {
  189.         try {
  190.             return entry().map(Object::toString).orElse("");
  191.         } catch (Exception e) {
  192.             log.error("Failed to convert entry to string!", e);
  193.         }
  194.         return "";
  195.     }

  196.     /**
  197.      *  The flag returned by this method indicates whether this cell
  198.      *  is selected or not. A cell is selected when the user interacts
  199.      *  with it, like clicking on it or navigating to it using the keyboard.
  200.      *  You may want to use this flag to change the appearance of the cell
  201.      *  when it is selected. For example, you may want to highlight the cell
  202.      *  by changing its background color.
  203.      *
  204.      * @return True if the cell is selected, false otherwise.
  205.      */
  206.     public boolean isSelected() {
  207.         return isSelected;
  208.     }

  209.     /**
  210.      *  Just like any other component, a cell may have focus or not.
  211.      *  The focus is typically indicated by a border around the cell.
  212.      *  It is an important property to consider when designing your cell
  213.      *  renderer, as you may want to change the appearance of the cell
  214.      *  when it has focus.
  215.      *
  216.      *  @return True if the cell has focus, false otherwise.
  217.      */
  218.     public boolean hasFocus() {
  219.         return hasFocus;
  220.     }

  221.     /**
  222.      *  This method returns true if the cell is currently being edited.
  223.      *  A cell is typically edited when the user double-clicks on it
  224.      *  or presses the F2 key. When a cell is being edited, then the cell
  225.      *  renderer wrapped by this cell will be used as an editor.
  226.      *  You may want to use this flag to change the appearance of the cell
  227.      *  when it is being edited. For example, you may want to show a text
  228.      *  field instead of a label when the cell is being edited.
  229.      *
  230.      * @return True if the cell is being edited, false otherwise.
  231.      *         Note that you can reliably say that when this flag
  232.      *         is true, then the cell builder is being used to construct
  233.      *         or maintain an editor.
  234.      */
  235.     public boolean isEditing() {
  236.         return isEditing;
  237.     }

  238.     /**
  239.      *  This method returns true if the cell is expanded, i.e. if it
  240.      *  is a parent cell in a {@link javax.swing.JTree}.
  241.      *  You may want to use this flag to change the appearance of the cell
  242.      *  when it is expanded.You may, for example, want to show a different
  243.      *  icon when the cell is expanded.
  244.      *
  245.      * @return True if the cell is expanded, false otherwise.
  246.      */
  247.     public boolean isExpanded() {
  248.         return isExpanded;
  249.     }

  250.     /**
  251.      *  This method returns true if the cell is a leaf, i.e. if it
  252.      *  is a child cell in a {@link javax.swing.JTree}.
  253.      *  You may want to use this flag to change the appearance of the cell
  254.      *  when it is a leaf. You may, for example, want to show a different
  255.      *  icon when the cell is a leaf.
  256.      *
  257.      * @return True if the cell is a leaf, false otherwise.
  258.      */
  259.     public boolean isLeaf() {
  260.         return isLeaf;
  261.     }

  262.     /**
  263.      *  Exposes a list of tool tips that should be shown when the user
  264.      *  hovers over the cell. The tool tips are strings that provide
  265.      *  additional information about the cell to the user.
  266.      *
  267.      * @return An unmodifiable list of tool tips that should be shown when the user hovers over the cell.
  268.      */
  269.     public List<String> toolTips() {
  270.         return Collections.unmodifiableList(toolTips);
  271.     }

  272.     /**
  273.      *  Gives you the row index of the cell in the table, list or drop down.
  274.      *  It tells you the location of the cell in the vertical direction.
  275.      *
  276.      * @return The row index of the cell in the table, list or drop down.
  277.      */
  278.     public int row() {
  279.         return row;
  280.     }

  281.     /**
  282.      *  Gives you the column index of the cell in the table, list or drop down.
  283.      *  It tells you the location of the cell in the horizontal direction.
  284.      *
  285.      * @return The column index of the cell in the table, list or drop down.
  286.      */
  287.     public int column() {
  288.         return column;
  289.     }

  290.     /**
  291.      *  Returns the renderer/editor of this cell, which is the component
  292.      *  that is used to display the cell to the user. The view
  293.      *  is typically a label, text field or some other custom component.
  294.      *  It is wrapped in an {@link Optional} to clearly indicate
  295.      *  that it may be null.<br>
  296.      *  Note that in case of the {@link CellConf#isEditing()} method
  297.      *  returning true, the view component stored in this cell is used as an editor.
  298.      *  If the cell is not being edited, then the component is used as a renderer.<br>
  299.      *  Two components are persisted across multiple calls to the
  300.      *  {@link CellBuilder}s {@link RenderAs#as(Configurator)} method, one
  301.      *  for the renderer and one for the editor. (So technically there are two views)<br>
  302.      *  Also note that not all types of components are suitable to
  303.      *  be used as editors. For example, a label is not suitable to be used as an editor.
  304.      *  Instead, you should use a text field or a combo box as an editor.<br>
  305.      *  If a component is not suitable to be used as an editor, then it
  306.      *  will simply be ignored in exchange for a default editor.
  307.      *
  308.      * @return An optional of the view of this cell, or an empty optional if the view is null.
  309.      *         In case of the {@link CellConf#isEditing()} method returning true,
  310.      *         the component stored in this optional is used as an editor.
  311.      *         The cell will remember the renderer and editor components across multiple calls
  312.      *         to the {@link CellBuilder}s {@link RenderAs#as(Configurator)} method.
  313.      */
  314.     public OptionalUI<Component> view() {
  315.         return OptionalUI.ofNullable(view);
  316.     }

  317.     /**
  318.      *  Allows you to configure the view of this cell by providing
  319.      *  a configurator lambda, which takes an {@link OptionalUI} of the
  320.      *  current renderer and returns a (potentially updated) {@link OptionalUI}
  321.      *  of the new renderer. <br>
  322.      *  The benefit of using this method is that you can easily initialize
  323.      *  the renderer with a new component through the {@link OptionalUI#orGetUi(Supplier)}
  324.      *  method, and then update it in every refresh coll inside the
  325.      *  {@link OptionalUI#update(java.util.function.Function)} method. <br>
  326.      *  This may look like the following:
  327.      *  <pre>{@code
  328.      *      UI.table()
  329.      *      .withCell(cell -> cell
  330.      *          .updateView( comp -> comp
  331.      *              .update( r -> { r.setText(cell.entryAsString()); return r; } )
  332.      *              .orGetUi( () -> UI.textField(cell.entryAsString()).withBackground(Color.CYAN) )
  333.      *          )
  334.      *      )
  335.      *      // ...
  336.      *  }</pre>
  337.      *  In this example, the view is initialized with a text field
  338.      *  if it is not present, and then the text field is continuously updated
  339.      *  with the entry of the cell. <br>
  340.      *
  341.      * @param configurator The {@link Configurator} lambda which takes an {@link OptionalUI}
  342.      *                     of the current view and returns a (potentially updated or initialized)
  343.      *                     {@link OptionalUI} of the new view.
  344.      * @return An updated cell delegate object with the new view.
  345.      *        If the configurator returns an empty optional, then the view
  346.      *        of the cell will be reset to null.
  347.      */
  348.     public CellConf<C,V> updateView( Configurator<OptionalUI<Component>> configurator ) {
  349.         OptionalUI<Component> newRenderer = OptionalUI.empty();
  350.         try {
  351.             newRenderer = configurator.configure(view());
  352.         } catch (Exception e) {
  353.             log.error("Failed to configure view!", e);
  354.         }
  355.         return _withRenderer(newRenderer.orElseNullable(null));
  356.     }

  357.     /**
  358.      *  Creates an updated cell delegate object with the given component
  359.      *  as the view (renderer/editor) of the cell. view is the
  360.      *  component that is used to render the cell to the user. It is
  361.      *  typically a label, text field or some other custom component.
  362.      *  <br>
  363.      *  Note that in case of the {@link CellConf#isEditing()} method
  364.      *  returning true, this {@link CellConf} is used for constructing
  365.      *  or maintaining an editor. If the cell is not being edited, then
  366.      *  this {@link CellConf} is used for rendering.<br>
  367.      *  Either way, the component is memorized across multiple calls to the
  368.      *  {@link CellBuilder}s {@link RenderAs#as(Configurator)} method. <br>
  369.      *
  370.      *  A typical usage of this method may look something like this:
  371.      *  <pre>{@code
  372.      *      UI.table()
  373.      *      .withCell(cell -> cell
  374.      *          .view(new JLabel("Hello, World!" + cell.row()) )
  375.      *      )
  376.      *      // ...
  377.      *  }</pre>
  378.      *  But keep in mind that in this example the label will be recreated
  379.      *  on every refresh call, which is not very efficient. It is better
  380.      *  to use the {@link CellConf#updateView(Configurator)} method to
  381.      *  initialize the view once and then update it in every refresh call.
  382.      *
  383.      * @param component The component to be used as the view of the cell.
  384.      * @return An updated cell delegate object with the new view to
  385.      *          serve as the renderer/editor of the cell.
  386.      */
  387.     public CellConf<C, V> view( Component component ) {
  388.         return _withRenderer(component);
  389.     }

  390.     /**
  391.      *  Creates an updated cell delegate object with the supplied cell
  392.      *  size and painter as the view (renderer/editor) of the cell.
  393.      *  The painter is a lambda that takes a {@link Graphics2D} object
  394.      *  and paints the cell with it.
  395.      *  This method is useful when you want to
  396.      *  create a custom cell renderer that paints the cell in a
  397.      *  specific way. For example, you may want to paint the cell
  398.      *  with a gradient background or a custom border.
  399.      *  <br>
  400.      *  Note that in case of the {@link CellConf#isEditing()} method
  401.      *  returning true, this {@link CellConf} is used for constructing
  402.      *  or maintaining an editor. If the cell is not being edited, then
  403.      *  this {@link CellConf} is used for rendering.<br>
  404.      *  Either way, the component is memorized across multiple calls to the
  405.      *  {@link CellBuilder}s {@link RenderAs#as(Configurator)} method.
  406.      *
  407.      * @param cellSize The minimum and preferred size of the cell to be painted.
  408.      * @param painter The lambda that paints the cell with a {@link Graphics2D} object.
  409.      * @return An updated cell delegate object with the new view to
  410.      *          serve as the renderer/editor of the cell.
  411.      */
  412.     public CellConf<C, V> renderer( Size cellSize, Consumer<Graphics2D> painter ) {
  413.         Component component = new Component() {
  414.             @Override
  415.             public void paint(Graphics g) {
  416.                 super.paint(g);
  417.                 painter.accept((Graphics2D) g);
  418.             }
  419.             /*
  420.                  The following methods are overridden as a performance measure
  421.                  to prune code-paths are often called in the case of renders
  422.                  but which we know are unnecessary.  Great care should be taken
  423.                  when writing your own renderer to weigh the benefits and
  424.                  drawbacks of overriding methods like these.
  425.              */
  426.             @Override
  427.             public boolean isOpaque() {
  428.                 Color back = getBackground();
  429.                 Component p = getParent();
  430.                 if (p != null) {
  431.                     p = p.getParent();
  432.                 }
  433.                 // p should now be the JTable.
  434.                 boolean colorMatch = (back != null) && (p != null) &&
  435.                         back.equals(p.getBackground()) &&
  436.                         p.isOpaque();
  437.                 return !colorMatch && super.isOpaque();
  438.             }
  439.             @Override
  440.             public void invalidate() {}
  441.             @Override
  442.             public void validate() {}
  443.             @Override
  444.             public void revalidate() {}
  445.             @Override
  446.             public void repaint(long tm, int x, int y, int width, int height) {}
  447.             @Override
  448.             public void repaint() {}
  449.             @Override
  450.             public void firePropertyChange(String propertyName, boolean oldValue, boolean newValue) { }
  451.         };
  452.         component.setMinimumSize(cellSize.toDimension());
  453.         component.setPreferredSize(cellSize.toDimension());
  454.         component.setSize(cellSize.toDimension());
  455.         return _withRenderer(component);
  456.     }

  457.     /**
  458.      *   Creates an updated cell delegate object with the default cell view / renderer
  459.      *   component based on the {@link javax.swing.DefaultListCellRenderer},
  460.      *   {@link javax.swing.table.DefaultTableCellRenderer} and {@link javax.swing.tree.DefaultTreeCellRenderer}
  461.      *   classes.
  462.      *
  463.      * @return An updated cell delegate object with the default view component.
  464.      *         This will override any custom view that was previously specified.
  465.      */
  466.     public CellConf<C, V> viewDefault() {
  467.         try {
  468.             return this.view(this.defaultRenderSource.get());
  469.         } catch (Exception e) {
  470.             log.error("Failed to create default renderer!", e);
  471.         }
  472.         return this;
  473.     }

  474.     public CellConf<C, V> _withRenderer(@Nullable Component component ) {
  475.         return new CellConf<>(
  476.             jListIfInvolved,
  477.             parent,
  478.             entry,
  479.             isSelected,
  480.             hasFocus,
  481.             isEditing,
  482.             isExpanded,
  483.             isLeaf,
  484.             row,
  485.             column,
  486.             component,
  487.             toolTips,
  488.             presentationEntry,
  489.             defaultRenderSource
  490.         );
  491.     }

  492.     /**
  493.      *  Creates a cell with an additional tool tip to be shown
  494.      *  when the user hovers over the cell. The tool tips are strings
  495.      *  that provide additional information about the cell to the user.
  496.      *
  497.      * @param toolTip The tool tip to be added to the list of tool tips.
  498.      * @return An updated cell delegate object with the new tool tip.
  499.      */
  500.     public CellConf<C, V> toolTip( String toolTip ) {
  501.         ArrayList<String> newToolTips = new ArrayList<>(toolTips);
  502.         newToolTips.add(toolTip);
  503.         return new CellConf<>(
  504.            jListIfInvolved,
  505.             parent,
  506.             entry,
  507.             isSelected,
  508.             hasFocus,
  509.             isEditing,
  510.             isExpanded,
  511.             isLeaf,
  512.             row,
  513.             column,
  514.             view,
  515.             newToolTips,
  516.             presentationEntry,
  517.             defaultRenderSource
  518.         );
  519.     }

  520.     /**
  521.      *  The presentation entry is the first choice of the
  522.      *  default cell view to be used for rendering and presentation
  523.      *  to the user. If it does not exist then the regular
  524.      *  cell entry is used for rendering by the default view.
  525.      *  Note that if you supply your own custom view/renderer component,
  526.      *  then the presentation entry is ignored.
  527.      *
  528.      * @return An optional of the presentation entry.
  529.      *         It may be an empty optional if no presentation entry was specified.
  530.      */
  531.     public Optional<Object> presentationEntry() {
  532.         return Optional.ofNullable(presentationEntry);
  533.     }

  534.     /**
  535.      *  The presentation entry is the first choice of the
  536.      *  default cell view to be used for rendering and presentation
  537.      *  to the user.
  538.      *  By default, this entry is null,
  539.      *  in which case it does not exist the regular
  540.      *  cell entry is used for rendering by the default view.
  541.      *  Note that if you supply a presentation value, then SwingTree
  542.      *  will try to apply this value to the view component.
  543.      *  (Which includes the editor and renderer components)
  544.      *
  545.      * @param toBeShown The object which should be used by the renderer
  546.      *                  to present to the user, typically a String.
  547.      * @return An updated cell delegate object with the new presentation entry.
  548.      */
  549.     public CellConf<C, V> presentationEntry( @Nullable Object toBeShown ) {
  550.         return new CellConf<>(
  551.            jListIfInvolved,
  552.             parent,
  553.             entry,
  554.             isSelected,
  555.             hasFocus,
  556.             isEditing,
  557.             isExpanded,
  558.             isLeaf,
  559.             row,
  560.             column,
  561.             view,
  562.             toolTips,
  563.             toBeShown,
  564.             defaultRenderSource
  565.         );
  566.     }

  567.     /**
  568.      *  The presentation entry is the first choice of the
  569.      *  default cell view to be used for rendering and presentation
  570.      *  to the user. A common use case is to convert the cell entry to
  571.      *  a presentation entry that is more suitable for the default view.
  572.      *  This method allows you to convert the cell entry to a presentation
  573.      *  entry by applying a function to it. The function takes the cell entry
  574.      *  as an argument and returns the presentation entry.
  575.      *  Note that if you supply a presentation value, then SwingTree
  576.      *  will try to apply this value to the view component.
  577.      *  (Which includes the editor and renderer components)
  578.      *
  579.      * @param presenter The function that converts the cell entry to a presentation entry.
  580.      * @return An updated cell delegate object with the new presentation entry.
  581.      * @throws NullPointerException If the presenter function is null.
  582.      */
  583.     public CellConf<C, V> entryToPresentation( Function<@Nullable V, @Nullable Object> presenter ) {
  584.         Objects.requireNonNull(presenter);
  585.         @Nullable V entry = this.entry;
  586.         try {
  587.             return presentationEntry(presenter.apply(entry));
  588.         } catch (Exception e) {
  589.             log.error("Failed to convert entry to presentation entry!", e);
  590.         }
  591.         return this;
  592.     }

  593. }