View Javadoc
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 javax.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   }