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