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