View Javadoc
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.impl.common;
27  
28  import jakarta.annotation.Nonnull;
29  import java.util.ArrayList;
30  import java.util.Collection;
31  import java.util.HashMap;
32  import java.util.List;
33  import java.util.Map;
34  import java.util.Optional;
35  import java.util.concurrent.CopyOnWriteArrayList;
36  import javafx.application.Platform;
37  import it.tidalwave.util.As;
38  import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
39  import it.tidalwave.ui.core.role.UserAction;
40  import lombok.Getter;
41  import lombok.extern.slf4j.Slf4j;
42  import static it.tidalwave.ui.core.role.UserActionProvider._UserActionProvider_;
43  import static java.util.stream.Collectors.*;
44  import static it.tidalwave.util.ShortNames.*;
45  
46  /***************************************************************************************************************************************************************
47   *
48   * This class collects a bag of roles from a source. It is used to preload roles in a generic thread before using them in a JavaFX thread.
49   *
50   * @author  Fabrizio Giudici
51   *
52   **************************************************************************************************************************************************************/
53  @Slf4j
54  public class RoleCollector
55    {
56      private final Map<Class<?>, List<Object>> roleMapByTipe = new HashMap<>();
57  
58      /** The default user action, which is the first action of the first {@link it.tidalwave.ui.core.role.UserActionProvider}. */
59      @Getter @Nonnull
60      private final Optional<UserAction> defaultUserAction;
61  
62      /***********************************************************************************************************************************************************
63       *
64       **********************************************************************************************************************************************************/
65      public RoleCollector()
66        {
67          defaultUserAction = Optional.empty();
68        }
69  
70      /***********************************************************************************************************************************************************
71       * Creates a new instances for the given source and role types.
72       * @param   source        the source
73       * @param   roleTypes     the types of roles to collect
74       **********************************************************************************************************************************************************/
75      @SuppressFBWarnings("CT_CONSTRUCTOR_THROW")
76      public RoleCollector (@Nonnull final As source, @Nonnull final Collection<Class<?>> roleTypes)
77        {
78          if (Platform.isFxApplicationThread())
79            {
80              throw new IllegalStateException("RoleCollector must not be instantiated in the JavaFX thread");
81            }
82  
83          if (log.isTraceEnabled())
84            {
85              log.trace("ctor({}, {})", source, shortNames(roleTypes));
86            }
87  
88          roleTypes.forEach(roleType -> copyRoles(source, roleType));
89          // TODO: perhaps it could be associated to a dummy key, instead of being returned by a getter?
90          defaultUserAction = getMany(_UserActionProvider_).stream().flatMap(a -> a.getOptionalDefaultAction().stream()).findFirst();
91        }
92  
93      /***********************************************************************************************************************************************************
94       * Adds a role. This method is useful for tests.
95       * @param   roleType      the type of the role
96       * @param   role          the role
97       **********************************************************************************************************************************************************/
98      public <R> void put (@Nonnull final Class<R> roleType, @Nonnull final R role)
99        {
100         putMany(roleType, List.of(role));
101       }
102 
103     /***********************************************************************************************************************************************************
104      * Adds roles. This method is useful for tests.
105      * @param   roleType      the type of the role
106      * @param   roles         the roles
107      **********************************************************************************************************************************************************/
108     public <R> void putMany (@Nonnull final Class<R> roleType, @Nonnull final Collection<? extends R> roles)
109       {
110         roleMapByTipe.put(roleType, new ArrayList<>(roles));
111       }
112 
113     /***********************************************************************************************************************************************************
114      * {@return a role of thw given type, if present}. If multiple roles for the same type are present, the first is returned.
115      * @param   roleType      the type of the role
116      **********************************************************************************************************************************************************/
117     @Nonnull
118     public <R> Optional<R> get (@Nonnull final Class<R> roleType)
119       {
120         return getMany(roleType).stream().findFirst();
121       }
122 
123     /***********************************************************************************************************************************************************
124      * {@return multiple roles of thw given type}.
125      * @param   roleType      the type of the role
126      **********************************************************************************************************************************************************/
127     @Nonnull @SuppressWarnings("unchecked")
128     public <R> List<R> getMany (@Nonnull final Class<R> roleType)
129       {
130         return new CopyOnWriteArrayList<>((List<R>)roleMapByTipe.getOrDefault(roleType, List.of()));
131       }
132 
133     /***********************************************************************************************************************************************************
134      * {@inheritDoc}
135      **********************************************************************************************************************************************************/
136     @Nonnull
137     public String toString()
138       {
139         return roleMapByTipe.entrySet().stream().map(this::toString).collect(joining(", ", "RoleCollector(", ")"));
140       }
141 
142     /***********************************************************************************************************************************************************
143      *
144      **********************************************************************************************************************************************************/
145     @Nonnull
146     private String toString (@Nonnull final Map.Entry<Class<?>, List<Object>> entry)
147       {
148         return shortName(entry.getKey()) + ": " + shortIds(entry.getValue());
149       }
150 
151     /***********************************************************************************************************************************************************
152      * Copy roles of the given type.
153      * @param   source        the source
154      * @param   roleType      the type of roles to collect
155      **********************************************************************************************************************************************************/
156     private <R> void copyRoles (@Nonnull final As source, @Nonnull final Class<R> roleType)
157       {
158         putMany(roleType, source.asMany(roleType));
159       }
160   }