Skip to content

Method: setup(PanelGroupControl.Configuration)

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.core.spi;
27:
28: import jakarta.annotation.Nonnull;
29: import jakarta.annotation.PostConstruct;
30: import jakarta.annotation.PreDestroy;
31: import java.util.ArrayList;
32: import java.util.Collection;
33: import java.util.HashMap;
34: import java.util.List;
35: import java.util.Map;
36: import java.util.Optional;
37: import java.util.function.Function;
38: import org.springframework.beans.factory.BeanFactory;
39: import it.tidalwave.ui.core.PanelGroupControl;
40: import it.tidalwave.ui.core.PanelGroupProvider;
41: import it.tidalwave.ui.core.message.PanelShowRequest;
42: import org.apiguardian.api.API;
43: import it.tidalwave.util.As;
44: import it.tidalwave.util.annotation.VisibleForTesting;
45: import it.tidalwave.messagebus.MessageBus;
46: import lombok.experimental.Delegate;
47: import lombok.extern.slf4j.Slf4j;
48: import static org.apiguardian.api.API.Status.EXPERIMENTAL;
49: import static java.util.stream.Collectors.*;
50: import static it.tidalwave.util.ShortNames.shortIds;
51:
52: /***************************************************************************************************************************************************************
53: *
54: * A support implementation of {@link PanelGroupControl}.
55: *
56: * @param <T> the type of the control
57: * @param <S> the type of the top container
58: * @since 2.0-ALPHA-3
59: * @author Fabrizio Giudici
60: *
61: **************************************************************************************************************************************************************/
62: @API(status = EXPERIMENTAL)
63: @Slf4j @SuppressWarnings("this-escape")
64: public abstract class PanelGroupControlSupport<T, S> implements As, PanelGroupControl<T>
65: {
66: @Delegate @Nonnull
67: private final As delegate = As.forObject(this);
68:
69: @Nonnull
70: private final Function<As, Collection<PanelGroupProvider<S>>> pgProvider;
71:
72: /** The message bus, if present on the system. */
73: @Nonnull
74: private final Optional<MessageBus> messageBus;
75:
76: /** The listener to the message bus. */
77: private final MessageBus.Listener<PanelShowRequest> messageListener = this::onShowRequest;
78:
79: /***********************************************************************************************************************************************************
80: * This class doesn't rely on the @SimpleSubscriber/@ListensTo annotations to avoid having a dependency on the MessageBus runtime, so it dynamically
81: * queries a {@link BeanFactory}.
82: **********************************************************************************************************************************************************/
83: protected PanelGroupControlSupport (@Nonnull final BeanFactory beanFactory, @Nonnull final String messageBusBeanName)
84: {
85: this(PanelGroupControlSupport::defaultPanelGroupProviders, beanFactory, messageBusBeanName);
86: }
87:
88: /***********************************************************************************************************************************************************
89: * Constructor for tets.
90: **********************************************************************************************************************************************************/
91: protected PanelGroupControlSupport (@Nonnull final Function<As, Collection<PanelGroupProvider<S>>> pgProvider,
92: @Nonnull final BeanFactory beanFactory,
93: @Nonnull final String messageBusBeanName)
94: {
95: this.pgProvider = pgProvider;
96: messageBus = getMessageBus(beanFactory, messageBusBeanName);
97: }
98:
99: /***********************************************************************************************************************************************************
100: * {@inheritDoc}
101: **********************************************************************************************************************************************************/
102: @Override
103: public void setup (@Nonnull final Configuration<T> configuration)
104: {
105: final var topContainersByGroup = configuration.getTopContainersByGroup();
106: final var providersByGroup = new HashMap<Group, List<PanelGroupProvider<S>>>();
107: pgProvider.apply(this).forEach(p -> providersByGroup.computeIfAbsent(p.getGroup(), ignored -> new ArrayList<>()).add(p));
108: log.debug("Providers by placement:");
109: providersByGroup.forEach((placement, provider) -> log.debug(">>>> {}: {}", placement, shortIds(provider)));
110: final var unboundGroups = providersByGroup.keySet().stream().filter(p -> !topContainersByGroup.containsKey(p)).collect(toList());
111:
112:• if (!unboundGroups.isEmpty())
113: {
114: throw new IllegalArgumentException("No top container(s) provider for " + unboundGroups);
115: }
116:
117: providersByGroup.forEach((group, providers) ->
118: assemble(group, providers, topContainersByGroup.get(group), configuration.getGroupOptions(), configuration.getOptions()));
119: }
120:
121: /***********************************************************************************************************************************************************
122: * Assemble a set of panes for the given group.
123: * @param group the {@code Group}
124: * @param panelProviders the {@code PanelGroupProvider}s associated to the given {@code Group}
125: * @param topContainer the top container
126: * @param groupOptions options for each group
127: * @param options options for doing the job
128: **********************************************************************************************************************************************************/
129: protected abstract void assemble (@Nonnull final Group group,
130: @Nonnull final List<? extends PanelGroupProvider<S>> panelProviders,
131: @Nonnull final T topContainer,
132: @Nonnull final Map<Group, List<Options>> groupOptions,
133: @Nonnull final List<Options> options);
134:
135: /***********************************************************************************************************************************************************
136: *
137: **********************************************************************************************************************************************************/
138: protected abstract void onShowRequest (@Nonnull PanelShowRequest panelShowRequest);
139:
140: /***********************************************************************************************************************************************************
141: *
142: **********************************************************************************************************************************************************/
143: protected void publish (@Nonnull final Object message)
144: {
145: messageBus.ifPresent(mb -> mb.publish(message));
146: }
147:
148: /***********************************************************************************************************************************************************
149: *
150: **********************************************************************************************************************************************************/
151: @PostConstruct
152: @VisibleForTesting void initialize()
153: {
154: messageBus.ifPresent(mb -> mb.subscribe(PanelShowRequest.class, messageListener));
155: }
156:
157: /***********************************************************************************************************************************************************
158: *
159: **********************************************************************************************************************************************************/
160: @PreDestroy
161: @VisibleForTesting void destroy()
162: {
163: messageBus.ifPresent(mb -> mb.unsubscribe(messageListener));
164: }
165:
166: /***********************************************************************************************************************************************************
167: *
168: **********************************************************************************************************************************************************/
169: @Nonnull
170: private static Optional<MessageBus> getMessageBus (@Nonnull final BeanFactory beanFactory, @Nonnull final String messageBusBeanName)
171: {
172: if (beanFactory.containsBean(messageBusBeanName))
173: {
174: return Optional.of(beanFactory.getBean(messageBusBeanName, MessageBus.class));
175: }
176: else
177: {
178: log.warn("No message bus");
179: return Optional.empty();
180: }
181: }
182:
183: /***********************************************************************************************************************************************************
184: *
185: **********************************************************************************************************************************************************/
186: @Nonnull
187: private static <S> Collection<PanelGroupProvider<S>> defaultPanelGroupProviders (@Nonnull final As as)
188: {
189: return as.asMany(As.<PanelGroupProvider<S>>type(PanelGroupProvider.class));
190: }
191: }