001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
002 *
003 * Copyright © 2025 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;
017import java.util.List;
018import java.util.NoSuchElementException;
019import java.util.Objects;
020
021import javax.lang.model.type.TypeMirror;
022
023import org.microbean.bean.AmbiguousReductionException;
024import org.microbean.bean.AttributedType;
025import org.microbean.bean.AutoCloseableRegistry;
026import org.microbean.bean.Bean;
027import org.microbean.bean.BeanException;
028import org.microbean.bean.Creation;
029import org.microbean.bean.DefaultAutoCloseableRegistry;
030import org.microbean.bean.Destruction;
031import org.microbean.bean.Factory;
032import org.microbean.bean.Id;
033import org.microbean.bean.Reducible;
034import org.microbean.bean.References;
035import org.microbean.bean.ReferencesSelector;
036import org.microbean.bean.Selectable;
037import org.microbean.bean.UnsatisfiedReductionException;
038
039import org.microbean.construct.Domain;
040
041/**
042 * A central object representing a request for dependencies that is a {@link Creation} (and therefore also a {@link
043 * Destruction}) and a {@link References}.
044 *
045 * <p>Instances of this class are the heart and soul of a dependency injection and acquisition system.</p>
046 *
047 * @param <I> the contextual instance type (see for example {@link Creation})
048 *
049 * @param <R> the contextual reference type (see {@link References})
050 *
051 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a>
052 *
053 * @see Creation
054 *
055 * @see Destruction
056 *
057 * @see References
058 *
059 * @see Factory#create(Creation)
060 *
061 * @see Factory#destroy(Object, Destruction)
062 */
063public final class Request<I, R> implements Creation<I>, Destruction, References<R> {
064
065
066  /*
067   * Instance fields.
068   */
069
070
071  private final Selectable<? super AttributedType, Bean<?>> s;
072
073  private final Instances instances;
074
075  private final AutoCloseableRegistry acr;
076
077  private final ClientProxier cp;
078
079  private final Bean<I> b;
080
081  private final AttributedType rType;
082
083
084  /*
085   * Constructors.
086   */
087
088
089  /**
090   * Creates a new {@link Request}.
091   *
092   * @param s a {@link Selectable} providing access to {@link Bean}s by {@link AttributedType}; must not be {@code
093   * null}; must be safe for concurrent use by multiple threads; often assembled out of methods present in the {@link
094   * org.microbean.bean.Selectables} and {@link org.microbean.bean.Beans} classes, among other such utility classes
095   *
096   * @param instances an {@link Instances} responsible for using a {@link Bean} to acquire an appropriate {@link
097   * java.util.function.Supplier} of contextual instances; must not be {@code null}
098   *
099   * @param acr an {@link AutoCloseableRegistry}; may be {@code null} in which case a default implementation will be
100   * used instead
101   *
102   * @param cp a {@link ClientProxier}; must not be {@code null}
103   *
104   * @exception NullPointerException if {@code s}, {@code instances}, or {@code cp} is {@code null}
105   */
106  public Request(final Selectable<? super AttributedType, Bean<?>> s,
107                 final Instances instances,
108                 final AutoCloseableRegistry acr,
109                 final ClientProxier cp) {
110    this(s, instances, acr, cp, null, null);
111  }
112
113  private Request(final Selectable<? super AttributedType, Bean<?>> s,
114                  final Instances instances,
115                  final AutoCloseableRegistry acr, // nullable
116                  final ClientProxier cp,
117                  final Bean<I> b,
118                  final AttributedType rType) { // the type of the references returned; nullable
119    this.s = Objects.requireNonNull(s, "s");
120    this.instances = Objects.requireNonNull(instances, "instances");
121    this.cp = Objects.requireNonNull(cp, "cp");
122    this.acr = acr == null ? new DefaultAutoCloseableRegistry() : acr;
123    this.b = b;
124    this.rType = rType;
125  }
126
127
128  /*
129   * Instance methods.
130   */
131
132
133  @Override // Destruction
134  public final void close() {
135    try (this.instances; this.acr) {}
136  }
137
138  @Override // Creation<I>
139  public final Id id() {
140    return this.b == null ? null : this.b.id();
141  }
142
143  @Override // References<R> (Iterable<R>)
144  public final Iterator<R> iterator() {
145    return new ReferencesIterator();
146  }
147
148  // Called by ReferencesIterator below
149  private final R get(final Request<R, Void> r) {
150    final Bean<R> bean = r.b;
151    final Id id = bean.id();
152    if (this.instances.proxiable(id)) {
153      final R ref = this.cp.clientProxy(id, instances.supplier(bean, r));
154      // TODO: we know that ref can be destroyed because it's from a normal scope, i.e. it is not itself a contextual
155      // instance, so save it in a collection somewhere so it can be destroyed via the #destroy(R) method (see CDI's
156      // Instances#destroy(Object))
157      return ref;
158    }
159    // TODO: ask instances if ref is destroyable and save it off
160    return instances.supplier(bean, r).get();
161  }
162
163  @Override // ReferencesSelector
164  public final <R> References<R> references(final AttributedType t) {
165    return new Request<>(this.s, this.instances, this.acr, this.cp, this.b, t);
166  }
167
168  @Override // References<R>
169  public final int size() {
170    return this.s.select(this.rType).size();
171  }
172
173  @Override // References<R>
174  public final boolean destroy(final R r) {
175    if (r != null) { // and is in dependent scope; we'll deal with that later
176      // TODO: remove it from a collection of dependent refs returned by get(Request) above
177    }
178    return false;
179  }
180
181
182  /*
183   * Inner and nested classes.
184   */
185
186
187  private final class ReferencesIterator implements Iterator<R> {
188
189    private Iterator<Bean<?>> i;
190
191    private R ref;
192
193    private ReferencesIterator() {
194      super();
195    }
196
197    @Override // Iterator<R>
198    public final boolean hasNext() {
199      if (rType == null) {
200        return false;
201      }
202      if (this.i == null) {
203        this.i = s.select(rType).iterator();
204      }
205      return this.i.hasNext();
206    }
207
208    @Override // Iterator<R>
209    public final R next() {
210      if (rType == null) {
211        throw new NoSuchElementException();
212      }
213      if (this.i == null) {
214        this.i = s.select(rType).iterator();
215      }
216      @SuppressWarnings("unchecked")
217      final R ref = get(new Request<>(s, instances, acr.newChild(), cp, (Bean<R>)this.i.next(), null));
218      this.ref = ref;
219      return ref;
220    }
221
222    @Override // Iterator<R>
223    public final void remove() {
224      final R ref = this.ref;
225      this.ref = null;
226      Request.this.destroy(ref);
227    }
228
229  }
230
231}