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}