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.DynamicConstantDesc;
018import java.lang.constant.MethodHandleDesc;
019
020import java.util.ArrayList;
021import java.util.Collections;
022import java.util.List;
023import java.util.Objects;
024import java.util.Optional;
025
026import javax.lang.model.type.TypeMirror;
027
028import org.microbean.constant.Constables;
029
030import org.microbean.qualifier.NamedAttributeMap;
031
032import org.microbean.scope.ScopeMember;
033
034import static java.lang.constant.ConstantDescs.BSM_INVOKE;
035import static java.lang.constant.ConstantDescs.CD_boolean;
036import static java.lang.constant.ConstantDescs.CD_int;
037import static java.lang.constant.ConstantDescs.CD_List;
038import static java.lang.constant.ConstantDescs.FALSE;
039import static java.lang.constant.ConstantDescs.TRUE;
040
041import static org.microbean.bean.BeanTypes.legalBeanType;
042import static org.microbean.bean.ConstantDescs.CD_Id;
043
044import static org.microbean.qualifier.ConstantDescs.CD_NamedAttributeMap;
045
046public final record Id(List<TypeMirror> types,
047                       List<NamedAttributeMap<?>> attributes,
048                       NamedAttributeMap<?> governingScopeId,
049                       boolean alternate,
050                       int rank)
051  implements Constable, Ranked, ScopeMember {
052
053  public Id(final List<TypeMirror> types,
054            final List<NamedAttributeMap<?>> attributes,
055            final NamedAttributeMap<?> governingScopeId) {
056    this(types, attributes, governingScopeId, false, Ranked.DEFAULT_RANK);
057  }
058
059  public Id(final List<TypeMirror> types,
060            final List<NamedAttributeMap<?>> attributes,
061            final NamedAttributeMap<?> governingScopeId,
062            final int rank) {
063    this(types, attributes, governingScopeId, false, rank);
064  }
065
066  public Id {
067    // The code below jumps through some hoops to avoid copying the types list if possible.
068    final int size = types.size();
069    if (size == 0) {
070      throw new IllegalArgumentException("types.isEmpty()");
071    }
072    int i = 0;
073    for (; i < size; i++) {
074      if (!legalBeanType(types.get(i))) {
075        break;
076      }
077    }
078    if (i == size) {
079      types = List.copyOf(types);
080    } else {
081      final ArrayList<TypeMirror> newTypes = new ArrayList<>(size);
082      for (int j = 0; j < i; j++) {
083        newTypes.add(types.get(j)); // the type is known to be legal
084      }
085      ++i; // skip past the illegal type i was pointing to
086      for (; i < size; i++) {
087        final TypeMirror t = types.get(i);
088        if (legalBeanType(t)) {
089          newTypes.add(t);
090        }
091      }
092      if (newTypes.isEmpty()) {
093        throw new IllegalArgumentException("types contains no legal bean types: " + types);
094      }
095      newTypes.trimToSize();
096      types = Collections.unmodifiableList(newTypes);
097    }
098    attributes = List.copyOf(attributes);
099    Objects.requireNonNull(governingScopeId, "governingScopeId");
100  }
101
102  @Override // Constable
103  public final Optional<DynamicConstantDesc<Id>> describeConstable() {
104    return Constables.describeConstable(this.attributes())
105      .flatMap(attributesDesc -> this.governingScopeId().describeConstable()
106               .flatMap(governingScopeIdDesc -> Constables.describeConstable(this.types())
107                        .map(typesDesc -> DynamicConstantDesc.of(BSM_INVOKE,
108                                                                 MethodHandleDesc.ofConstructor(CD_Id,
109                                                                                                CD_List,
110                                                                                                CD_List,
111                                                                                                CD_NamedAttributeMap,
112                                                                                                CD_boolean,
113                                                                                                CD_int),
114                                                                 typesDesc,
115                                                                 attributesDesc,
116                                                                 governingScopeIdDesc,
117                                                                 this.alternate() ? TRUE : FALSE,
118                                                                 this.rank()))));
119  }
120
121}