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