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