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}