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.producer;
015
016import java.util.LinkedHashSet;
017import java.util.SequencedSet;
018
019import java.util.function.Function;
020
021import javax.lang.model.element.Element;
022
023import org.microbean.assign.Aggregate;
024import org.microbean.assign.Annotated;
025import org.microbean.assign.Assignment;
026
027import org.microbean.bean.Creation;
028import org.microbean.bean.Destruction;
029import org.microbean.bean.Id;
030import org.microbean.bean.ReferencesSelector;
031
032import static java.util.Collections.unmodifiableSequencedSet;
033
034/**
035 * An interface whose implementations {@linkplain #produce(Creation) produce} and commonly initialize contextual
036 * instances.
037 *
038 * <p>{@link Producer}s are used to implement {@link org.microbean.bean.Factory} instances' {@link
039 * org.microbean.bean.Factory#create(Creation) create(Creation)} and {@link org.microbean.bean.Factory#destroy(Object,
040 * Destruction) destroy(Object, Destruction)} methods.</p>
041 *
042 * <p>A {@link Producer} normally initializes the contextual instances it produces as part of its {@link
043 * #produce(Creation)} method implementation, but is not required to.</p>
044 *
045 * @param <I> the contextual instance type
046 *
047 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a>
048 *
049 * @see #produce(Creation)
050 *
051 * @see #dependencies()
052 *
053 * @see #dispose(Object, Destruction)
054 *
055 * @see org.microbean.bean.Factory#create(Creation)
056 */
057// Subordinate to Factory<I> (but looking more and more like it every day)
058// Akin to CDI's Producer.
059// Handles instance production, interception and disposal, *including intercepted production*.
060//
061// Also handles initialization. We may want to revisit this. See for example
062// https://github.com/search?q=repo%3Aweld%2Fcore+%22.produce%28%29%22+language%3AJava&type=code
063//
064// See also: InterceptingProducer
065public interface Producer<I> extends Aggregate {
066
067  /**
068   * A convenience method that returns an immutable, determinate {@link SequencedSet} of {@link Element}s
069   * consisting of this {@link Producer}'s {@linkplain #productionDependencies() production dependencies} followed by
070   * its {@linkplain #initializationDependencies() initialization dependencies}.
071   *
072   * <p><strong>There is normally no need to override the default implementation of this method.</strong></p>
073   *
074   * @return a non-{@code null}, immutable, determinate {@link SequencedSet} of {@link Element}s consisting of
075   * this {@link Producer}'s {@linkplain #productionDependencies() production dependencies} followed by its {@linkplain
076   * #initializationDependencies() initialization dependencies}
077   *
078   * @see #productionDependencies()
079   *
080   * @see #initializationDependencies()
081   */
082  @Override // Aggregate
083  public default SequencedSet<? extends Annotated<? extends Element>> dependencies() {
084    final SequencedSet<? extends Annotated<? extends Element>> productionDependencies = this.productionDependencies();
085    if (productionDependencies.isEmpty()) {
086      return this.initializationDependencies();
087    }
088    final SequencedSet<? extends Annotated<? extends Element>> initializationDependencies = this.initializationDependencies();
089    if (initializationDependencies.isEmpty()) {
090      return productionDependencies;
091    }
092    final LinkedHashSet<Annotated<? extends Element>> d = new LinkedHashSet<>();
093    d.addAll(productionDependencies);
094    d.addAll(initializationDependencies);
095    return unmodifiableSequencedSet(d);
096  }
097
098  /**
099   * Disposes of the supplied contextual instance.
100   *
101   * <p>The default implementation of this method checks to see if {@code i} is an instance of {@link AutoCloseable},
102   * and, if so, calls {@link AutoCloseable#close() close()} on it, throwing any resulting exception as a {@link
103   * DisposalException}.</p>
104   *
105   * @param i a contextual instance {@linkplain #produce(Creation) produced} by this {@link Producer}; may be {@code
106   * null}
107   *
108   * @param r the {@link Creation} that was {@linkplain #produce(Creation) present at production time}; must not be {@code
109   * null}
110   *
111   * @exception NullPointerException if {@code r} is {@code null}
112   *
113   * @exception DisposalException if {@code i} is an {@link AutoCloseable} instance, and if its {@link
114   * AutoCloseable#close() close()} method throws a checked exception
115   */
116  public default void dispose(final I i, final Destruction r) {
117    if (i instanceof AutoCloseable ac) {
118      try {
119        ac.close();
120      } catch (final RuntimeException | Error e) {
121        throw e;
122      } catch (final InterruptedException e) {
123        Thread.currentThread().interrupt();
124        throw new DisposalException(e.getMessage(), e);
125      } catch (final Exception e) {
126        throw new DisposalException(e.getMessage(), e);
127      }
128    }
129  }
130
131  /**
132   * Returns an immutable, determinate {@link SequencedSet} of {@link Element}s representing dependencies
133   * required for <dfn>initialization</dfn>.
134   *
135   * <p>Such dependencies may represent initialization method parameters and/or fields.</p>
136   *
137   * <p>Contrast initialization dependencies with <dfn>production dependencies</dfn>.</p>
138   *
139   * @return a non-{@code null}, immutable, determinate {@link SequencedSet} of {@link Element}s representing
140   * dependencies required for initialization
141   *
142   * @see #productionDependencies()
143   */
144  public SequencedSet<? extends Annotated<? extends Element>> initializationDependencies();
145
146  /**
147   * Produces a new contextual instance and returns it by calling the {@link #produce(Id, SequencedSet)} method with the
148   * return value of an invocation of the {@link #assign(Function)} method with a reference to the supplied {@link
149   * Creation}'s {@link ReferencesSelector#reference(TypeMirror) reference(TypeMirror)} method.
150   *
151   * <p><strong>There is normally no need to override the default implementation of this method.</strong></p>
152   *
153   * @param c a {@link Creation}; must not be {@code null}
154   *
155   * @return a new contextual instance, or {@code null}
156   *
157   * @exception NullPointerException if {@code c} is {@code null}
158   *
159   * @see #produce(Id, SequencedSet)
160   *
161   * @see #dependencies()
162   */
163  public default I produce(final Creation<I> c) {
164    return this.produce(c.id(), this.assign(c::reference));
165  }
166
167  /**
168   * Produces a new contextual instance and returns it, possibly (often) making use of the supplied assignments.
169   *
170   * <p>Implementations of this method must not call {@link #produce(Creation)} or an infinite loop may result.</p>
171   *
172   * @param id an {@link Id} for which production is occurring; must not be {@code null}
173   *
174   * @param assignments a {@link SequencedSet} of {@link Assignment}s this {@link Producer} needs to complete production
175   * and possibly initialization; must not be {@code null}
176   *
177   * @return a new contextual instance, or {@code null}
178   *
179   * @exception NullPointerException if {@code assignments} is {@code null}
180   */
181  public I produce(final Id id, final SequencedSet<? extends Assignment<?>> assignments);
182
183  /**
184   * Returns an immutable, determinate {@link SequencedSet} of {@link Element}s representing dependencies required for
185   * <dfn>production</dfn>.
186   *
187   * <p>Such dependencies normally represent constructor parameters.</p>
188   *
189   * <p>Contrast production dependencies with <dfn>initialization dependencies</dfn>.</p>
190   *
191   * @return a non-{@code null}, immutable, determinate {@link SequencedSet} of {@link Element}s representing
192   * dependencies required for production
193   *
194   * @see #initializationDependencies()
195   */
196  // TODO: we could posit that the first Element in the set could return a TypeElement from its element()
197  // method. If so, then that thing's type ((DeclaredType)asType()) could be used to form an TypeMirror to locate
198  // the contextual reference on which to, for example, invoke a method if this Producer represents a producer method
199  public SequencedSet<? extends Annotated<? extends Element>> productionDependencies();
200
201}