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 }