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}