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.bean; 015 016import java.lang.constant.ClassDesc; 017import java.lang.constant.Constable; 018import java.lang.constant.ConstantDesc; 019import java.lang.constant.DynamicConstantDesc; 020 021import java.util.Optional; 022import java.util.SequencedSet; 023 024import java.util.function.Function; 025 026import javax.lang.model.AnnotatedConstruct; 027 028import javax.lang.model.element.Element; 029 030import org.microbean.assign.Aggregate; 031import org.microbean.assign.Annotated; 032import org.microbean.assign.Assignment; 033 034import static java.lang.constant.ConstantDescs.BSM_INVOKE; 035 036import static java.lang.constant.MethodHandleDesc.ofConstructor; 037 038import static java.util.Objects.requireNonNull; 039 040/** 041 * A ({@link Constable}) pairing of an {@link Id} with a {@link Factory}. 042 * 043 * @param <I> the type of the contextual instances the associated {@link Factory} creates 044 * 045 * @param id the {@link Id}; must not be {@code null} 046 * 047 * @param factory the {@link Factory}; must not be {@code null} 048 * 049 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a> 050 * 051 * @see Factory 052 * 053 * @see Id 054 */ 055public final record Bean<I>(Id id, Factory<I> factory) implements Aggregate, Constable { 056 057 /** 058 * Creates a new {@link Bean}. 059 * 060 * @param id the {@link Id}; must not be {@code null} 061 * 062 * @param factory the {@link Factory}; must not be {@code null} 063 * 064 * @exception NullPointerException if either argument is {@code null} 065 */ 066 public Bean { 067 requireNonNull(id, "id"); 068 requireNonNull(factory, "factory"); 069 } 070 071 // (Convenience.) 072 @Override // Aggregate 073 public final SequencedSet<? extends Assignment<?>> assign(final Function<? super Annotated<? extends AnnotatedConstruct>, ?> r) { 074 return this.factory.assign(r); 075 } 076 077 /** 078 * Returns this {@link Bean}, forcibly cast appropriately. 079 * 080 * @param <X> the type of contextual instances this {@link Bean} creates 081 * 082 * @return this {@link Bean} 083 * 084 * @exception ClassCastException if the cast is inappropriate 085 */ 086 @SuppressWarnings("unchecked") 087 public final <X> Bean<X> cast() { 088 return (Bean<X>)this; 089 } 090 091 // (Convenience.) 092 @Override // Aggregate 093 public final SequencedSet<? extends Annotated<? extends Element>> dependencies() { 094 return this.factory.dependencies(); 095 } 096 097 @Override // Constable 098 public final Optional<DynamicConstantDesc<Bean<I>>> describeConstable() { 099 return (this.factory instanceof Constable c ? c.describeConstable() : Optional.<ConstantDesc>empty()) 100 .flatMap(factoryDesc -> this.id.describeConstable() 101 .map(idDesc -> DynamicConstantDesc.of(BSM_INVOKE, 102 ofConstructor(ClassDesc.of(Bean.class.getName()), 103 ClassDesc.of(Id.class.getName()), 104 ClassDesc.of(Factory.class.getName())), 105 idDesc, 106 factoryDesc))); 107 } 108 109 @Override // Record (Object) 110 public final boolean equals(final Object other) { 111 return this == other || switch (other) { 112 case null -> false; 113 case Bean<?> b when this.getClass() == b.getClass() -> this.id.equals(b.id); 114 default -> false; 115 }; 116 } 117 118 @Override // Record (Object) 119 public final int hashCode() { 120 return this.id.hashCode(); 121 } 122 123}