1 /*
2 * *************************************************************************************************************************************************************
3 *
4 * TheseFoolishThings: Miscellaneous utilities
5 * http://tidalwave.it/projects/thesefoolishthings
6 *
7 * Copyright (C) 2009 - 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/thesefoolishthings-src
22 * git clone https://github.com/tidalwave-it/thesefoolishthings-src
23 *
24 * *************************************************************************************************************************************************************
25 */
26 package it.tidalwave.role.impl;
27
28 import jakarta.annotation.Nonnull;
29 import java.util.ArrayList;
30 import java.util.Collection;
31 import java.util.Collections;
32 import java.util.List;
33 import java.util.Optional;
34 import java.util.function.Function;
35 import it.tidalwave.util.As;
36 import it.tidalwave.util.LazySupplier;
37 import it.tidalwave.util.Parameters;
38 import it.tidalwave.util.RoleFactory;
39 import it.tidalwave.role.spi.OwnerRoleFactory;
40 import it.tidalwave.role.spi.OwnerRoleFactoryProvider;
41 import static it.tidalwave.util.Parameters.r;
42
43 /***************************************************************************************************************************************************************
44 *
45 * An implementation of {@link As} that keeps some local roles and queries {@link OwnerRoleFactory}.
46 *
47 * @author Fabrizio Giudici
48 *
49 **************************************************************************************************************************************************************/
50 public class AsDelegate implements As
51 {
52 @Nonnull
53 private final Object owner;
54
55 @Nonnull
56 private final LazySupplier<OwnerRoleFactory> ownerRoleFactory;
57
58
59 @Nonnull
60 private final List<Object> roles = new ArrayList<>();
61
62 /***********************************************************************************************************************************************************
63 * Constructor for use in subclassing.
64 **********************************************************************************************************************************************************/
65 protected AsDelegate ()
66 {
67 owner = this;
68 ownerRoleFactory = LazySupplier.of(() -> OwnerRoleFactoryProvider.getInstance().createRoleFactory(this));
69 }
70
71 /***********************************************************************************************************************************************************
72 * Constructor for use in composition.
73 *
74 * @param owner the owner
75 * @since 3.2-ALPHA-3 (refactored)
76 **********************************************************************************************************************************************************/
77 public AsDelegate (@Nonnull final Object owner)
78 {
79 this(owner, Collections.emptyList());
80 }
81
82 /***********************************************************************************************************************************************************
83 * Constructor for use in composition. In addition to the mandatory owner, it accepts a single pre-instantiated
84 * role, or a {@link RoleFactory} that will be invoked to create additional roles.
85 *
86 * @param owner the owner
87 * @param role the role or {@link it.tidalwave.util.RoleFactory}
88 * @since 3.2-ALPHA-3
89 **********************************************************************************************************************************************************/
90 public AsDelegate (@Nonnull final Object owner, @Nonnull final Object role)
91 {
92 this(owner, r(Parameters.mustNotBeArrayOrCollection(role, "role")));
93 }
94
95 /***********************************************************************************************************************************************************
96 * Constructor for use in composition. In addition to the mandatory owner, it accepts a collection of
97 * pre-instantiated roles, or instances of {@link RoleFactory} that will be invoked to create additional roles.
98 *
99 * @param owner the owner
100 * @param roles roles or {@link it.tidalwave.util.RoleFactory} instances
101 * @since 3.2-ALPHA-3 (refactored)
102 **********************************************************************************************************************************************************/
103 public AsDelegate (@Nonnull final Object owner, @Nonnull final Collection<Object> roles)
104 {
105 this(o -> OwnerRoleFactoryProvider.getInstance().createRoleFactory(o), owner, roles);
106 }
107
108 /***********************************************************************************************************************************************************
109 * Constructor for use in tests. This constructor doesn't call {@link OwnerRoleFactoryProvider#getInstance()}.
110 *
111 * @param systemRoleFactoryFunction the factory
112 * @param owner the owner
113 * @param roles roles or {@link it.tidalwave.util.RoleFactory} instances
114 * @since 3.2-ALPHA-3 (refactored)
115 **********************************************************************************************************************************************************/
116 public AsDelegate (@Nonnull final Function<Object, OwnerRoleFactory> systemRoleFactoryFunction,
117 @Nonnull final Object owner,
118 @Nonnull final Collection<Object> roles)
119 {
120 ownerRoleFactory = LazySupplier.of(() -> systemRoleFactoryFunction.apply(owner));
121 this.owner = owner;
122 this.roles.addAll(RoleFactory.resolveFactories(owner, roles));
123 }
124
125 /***********************************************************************************************************************************************************
126 * {@inheritDoc}
127 *
128 * First, local roles are probed; then the owner, in case it directly implements the required role; at last,
129 * the systemRoleFactory is invoked.
130 **********************************************************************************************************************************************************/
131 @Override @Nonnull
132 public <T> Optional<T> maybeAs (@Nonnull final Class<? extends T> type)
133 {
134 for (final var role : roles)
135 {
136 if (type.isAssignableFrom(role.getClass()))
137 {
138 return Optional.of(type.cast(role));
139 }
140 }
141
142 final var r = ownerRoleFactory.get().findRoles(type);
143 return r.isEmpty() ? Optional.empty() : Optional.of(r.iterator().next());
144 }
145
146 /***********************************************************************************************************************************************************
147 * {@inheritDoc}
148 *
149 * The list contains all the relevant local roles, as well as those retrieved by the delegate, in this order.
150 **********************************************************************************************************************************************************/
151 @Nonnull
152 public <T> Collection<T> asMany (@Nonnull final Class<? extends T> type)
153 {
154 final var results = new ArrayList<T>();
155
156 for (final var role : roles)
157 {
158 if (type.isAssignableFrom(role.getClass()))
159 {
160 results.add(type.cast(role));
161 }
162 }
163
164 results.addAll(ownerRoleFactory.get().findRoles(type));
165
166 return results;
167 }
168 }