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