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}