001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
002 *
003 * Copyright © 2025–2026 microBean™.
004 *
005 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
006 * the License. You may obtain a copy of the License at
007 *
008 *     http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
011 * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
012 * specific language governing permissions and limitations under the License.
013 */
014package org.microbean.reference;
015
016import java.util.Iterator;
017
018import java.util.function.Supplier;
019
020import javax.lang.model.AnnotatedConstruct;
021
022import javax.lang.model.type.TypeMirror;
023
024import org.microbean.assign.Annotated;
025import org.microbean.assign.Selectable;
026
027import org.microbean.bean.Bean;
028import org.microbean.bean.BeanException;
029import org.microbean.bean.Creation;
030import org.microbean.bean.Destruction;
031import org.microbean.bean.Id;
032import org.microbean.bean.References;
033import org.microbean.bean.ReferencesSelector;
034
035import org.microbean.construct.Domain;
036
037import org.microbean.proxy.Proxy;
038
039import static java.util.Collections.emptyIterator;
040
041import static java.util.Objects.requireNonNull;
042
043/**
044 * A central object representing a request for dependencies that is a {@link Creation} (and therefore also, per
045 * contract, a {@link Destruction}), a {@link DestructorRegistry}, and a {@link References}.
046 *
047 * <p>Instances of this class are the heart and soul of a dependency injection and acquisition system.</p>
048 *
049 * @param <I> the contextual instance type being instantiated (see for example {@link Creation})
050 *
051 * @param <R> the contextual reference type being sought (see {@link References})
052 *
053 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a>
054 *
055 * @see Creation
056 *
057 * @see Destruction
058 *
059 * @see DestructorRegistry
060 *
061 * @see References
062 *
063 * @see org.microbean.bean.Factory#create(Creation)
064 *
065 * @see org.microbean.bean.Factory#destroy(Object, Destruction)
066 */
067public final class Request<I, R> implements Creation<I>, Destruction, DestructorRegistry, References<R> {
068
069
070  /*
071   * Instance fields.
072   */
073
074
075  private final Domain domain;
076
077  private final Selectable<? super Annotated<? extends AnnotatedConstruct>, Bean<?>> beans;
078
079  private final Instances instances;
080
081  private final DestructorTree destructorTree;
082
083  private final ClientProxier cp;
084
085  private final Bean<I> b; // nullable; B and R must then be (effectively) Void
086
087  private final Annotated<? extends AnnotatedConstruct> rConstruct; // nullable; R must then be Void
088
089
090  /*
091   * Constructors.
092   */
093
094
095  /**
096   * Creates a new {@link Request}.
097   *
098   * @param d a {@link Domain}; must not be {@code null}
099   *
100   * @param s a {@link Selectable} providing access to {@link Bean}s by {@link AnnotatedConstruct}; must not be {@code
101   * null}; must be safe for concurrent use by multiple threads; often assembled out of methods present in the {@link
102   * org.microbean.bean.Selectables} and {@link org.microbean.bean.Beans} classes, among other such utility classes
103   *
104   * @param instances an {@link Instances} responsible for using a {@link Bean} to acquire an appropriate {@link
105   * Supplier} of contextual instances; must not be {@code null}
106   *
107   * @param cp a {@link ClientProxier}; must not be {@code null}
108   *
109   * @exception NullPointerException if any argument is {@code null}
110   *
111   * @see #Request(Domain, Selectable, Instances, DestructorTree, ClientProxier)
112   */
113  public Request(final Domain d,
114                 final Selectable<? super Annotated<? extends AnnotatedConstruct>, Bean<?>> s,
115                 final Instances instances,
116                 final ClientProxier cp) {
117    this(d, s, instances, null, cp, null, null);
118  }
119
120  /**
121   * Creates a new {@link Request}.
122   *
123   * @param d a {@link Domain}; must not be {@code null}
124   *
125   * @param s a {@link Selectable} providing access to {@link Bean}s by {@link AnnotatedConstruct}; must not be {@code
126   * null}; must be safe for concurrent use by multiple threads; often assembled out of methods present in the {@link
127   * org.microbean.bean.Selectables} and {@link org.microbean.bean.Beans} classes, among other such utility classes
128   *
129   * @param instances an {@link Instances} responsible for using a {@link Bean} to acquire an appropriate {@link
130   * Supplier} of contextual instances; must not be {@code null}
131   *
132   * @param destructorTree a {@link DestructorTree}; may be {@code null} in which case a default implementation will be used
133   * instead
134   *
135   * @param cp a {@link ClientProxier}; must not be {@code null}
136   *
137   * @exception NullPointerException if {@code s}, {@code instances}, or {@code cp} is {@code null}
138   */
139  public Request(final Domain d,
140                 final Selectable<? super Annotated<? extends AnnotatedConstruct>, Bean<?>> s,
141                 final Instances instances,
142                 final DestructorTree destructorTree, // nullable
143                 final ClientProxier cp) {
144    this(d, s, instances, destructorTree, cp, null, null);
145  }
146
147  private Request(final Domain d,
148                  final Selectable<? super Annotated<? extends AnnotatedConstruct>, Bean<?>> s,
149                  final Instances instances,
150                  final DestructorTree destructorTree, // nullable
151                  final ClientProxier cp,
152                  final Bean<I> b, // nullable
153                  final Annotated<? extends AnnotatedConstruct> rConstruct) { // the type of the references returned (<R>); nullable
154    this.domain = requireNonNull(d, "d");
155    this.beans = requireNonNull(s, "s");
156    this.instances = requireNonNull(instances, "instances");
157    this.cp = requireNonNull(cp, "cp");
158    this.destructorTree = destructorTree == null ? new DefaultDestructorTree() : destructorTree;
159    this.b = b;
160    this.rConstruct = rConstruct;
161  }
162
163
164  /*
165   * Instance methods.
166   */
167
168
169  @Override // Destruction
170  public final void close() {
171    this.destructorTree.close();
172  }
173
174  @Override // ReferencesSelector
175  public final boolean destroy(final Object r) {
176    final Destructor destructor = this.destructorTree.remove(r instanceof Proxy<?> p ? p.$proxied() : r);
177    if (destructor != null) {
178      destructor.destroy(); // I keep going back and forth on whether this should be under some kind of lock, or whether the Destructor contract covers it
179      return true;
180    }
181    return false;
182  }
183
184  @Override // ReferencesSelector
185  public final Domain domain() {
186    return this.domain;
187  }
188
189  @Override // Creation<I>
190  public final Id id() {
191    return this.b == null ? null : this.b.id();
192  }
193
194  @Override // References<R> (Iterable<R>)
195  public final Iterator<R> iterator() {
196    return new ReferencesIterator(); // inner class; see below
197  }
198
199  @Override // ReferencesSelector
200  public final <R> R reference(final Bean<R> bean) {
201    final Supplier<? extends R> supplier = this.instances.supplier(bean, this.newChild(bean)); // newChild is critical
202    final Id id = bean.id();
203    return this.instances.proxiable(id) ? this.cp.clientProxy(id, supplier) : supplier.get();
204  }
205
206  @Override // ReferencesSelector
207  @SuppressWarnings("unchecked")
208  public final <X> References<X> references(final Annotated<? extends AnnotatedConstruct> rConstruct) {    
209    return this.rConstruct == rConstruct ? (References<X>)this :
210      // This basically returns "this" but with a new rConstruct. But Request is immutable so we make a copy.
211      new Request<>(this.domain,
212                     this.beans,
213                     this.instances,
214                     this.destructorTree, // deliberately NO this.destructorTree.newChild() call
215                     this.cp,
216                     this.b, // nullable; <I> will then be (effectively) Void
217                     rConstruct); // nullable; <X> will then be Void
218  }
219
220  @Override // DestructorTree (DestructorRegistry)
221  public final boolean register(final Object reference, final Destructor destructor) {
222    return this.destructorTree.register(reference, destructor);
223  }
224
225  @Override // References<R>
226  public final int size() {
227    return this.rConstruct == null ? 0 : this.beans.select(this.rConstruct).size();
228  }
229
230  /*
231   * Private instance methods.
232   */
233
234  private final Iterator<Bean<?>> beanIterator() {
235    return this.rConstruct == null ? emptyIterator() : this.beans.select(this.rConstruct).iterator();
236  }
237
238  @SuppressWarnings("unchecked")
239  private final <X> Request<X, ?> newChild(final Bean<X> b) {
240    if (b == null) {
241      if (this.b == null) {
242        return (Request<X, R>)this; // both <X> and <R> are effectively Void
243      }
244    } else if (b.equals(this.b)) {
245      return (Request<X, R>)this;
246    }
247    return
248      new Request<X, Void>(this.domain,
249                            this.beans,
250                            this.instances,
251                            this.destructorTree.newChild(), // critical; !b.equals(this.b)
252                            this.cp,
253                            b, // nullable; if so, <X> better resolve to Void
254                            null); // rConstruct; <R> resolves to Void
255  }
256
257
258  /*
259   * Inner and nested classes.
260   */
261
262
263  // NOT thread-safe.
264  private final class ReferencesIterator implements Iterator<R> {
265
266    private Iterator<Bean<?>> i;
267
268    private R ref;
269
270    private ReferencesIterator() {
271      super();
272      if (Request.this.rConstruct == null) {
273        this.i = emptyIterator();
274      }
275    }
276
277    @Override // Iterator<R>
278    public final boolean hasNext() {
279      if (this.i == null) {
280        this.i = beanIterator();
281      }
282      return this.i.hasNext();
283    }
284
285    @Override // Iterator<R>
286    @SuppressWarnings("unchecked")
287    public final R next() {
288      if (this.i == null) {
289        this.i = beanIterator();
290      }
291      return this.ref = reference((Bean<R>)this.i.next());
292    }
293
294    @Override // Iterator<R>
295    public final void remove() {
296      final R ref = this.ref;
297      if (ref == null) {
298        throw new IllegalStateException(); // per Iterator#remove() contract
299      }
300      this.ref = null;
301      destroy(ref);
302    }
303
304  }
305
306}