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 }