001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
002 *
003 * Copyright © 2023 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.qualifier;
015
016import java.lang.constant.Constable;
017import java.lang.constant.DynamicConstantDesc;
018import java.lang.constant.MethodHandleDesc;
019import java.lang.constant.MethodTypeDesc;
020
021import java.util.ArrayList;
022import java.util.Collection;
023import java.util.Collections;
024import java.util.List;
025import java.util.Objects;
026import java.util.Optional;
027
028import org.microbean.constant.Constables;
029
030import org.microbean.invoke.ContentHashable;
031
032import static java.lang.constant.ConstantDescs.BSM_INVOKE;
033import static java.lang.constant.ConstantDescs.CD_Collection;
034import static java.lang.constant.ConstantDescs.CD_Object;
035import static java.lang.constant.DirectMethodHandleDesc.Kind.STATIC;
036
037import static org.microbean.qualifier.ConstantDescs.CD_Attributed;
038
039public record Attributed<T, V>(Collection<NamedAttributeMap<V>> attributes, T attributed) implements AttributeBearing<V>, Constable, ContentHashable {
040
041  @SuppressWarnings("unchecked")
042  public Attributed {
043    Objects.requireNonNull(attributed, "attributed");
044    if (attributes.isEmpty()) {
045      attributes = List.of();
046    } else {
047      final List<NamedAttributeMap<V>> l =
048        attributes instanceof ArrayList<NamedAttributeMap<V>> cloneMe ?
049        (List<NamedAttributeMap<V>>)cloneMe.clone() :
050        new ArrayList<>(attributes);
051      Collections.sort(l);
052      attributes = Collections.unmodifiableList(l);
053    }
054  }
055
056  @Override // ContentHashable
057  public final Optional<String> contentHashInput() {
058    final StringBuilder sb = new StringBuilder();
059    for (final NamedAttributeMap<V> a : this.attributes()) {
060      sb.append(a.contentHashInput().orElse(""));
061    }
062    final Object attributed = this.attributed();
063    if (attributed instanceof ContentHashable ch) {
064      final Optional<?> o = ch.contentHashInput();
065      if (o.isPresent()) {
066        sb.append(o.orElseThrow());
067      }
068    } else if (attributed != null) {
069      sb.append(attributed.toString());
070    }
071    return Optional.of(sb.toString());
072  }
073
074  @Override // Constable
075  public final Optional<DynamicConstantDesc<Attributed<T, V>>> describeConstable() {
076    return Constables.describeConstable(this.attributed())
077      .flatMap(attributedDesc -> Constables.describeConstable(this.attributes())
078               .map(attributesDesc -> DynamicConstantDesc.of(BSM_INVOKE,
079                                                             MethodHandleDesc.ofMethod(STATIC,
080                                                                                       CD_Attributed,
081                                                                                       "of",
082                                                                                       MethodTypeDesc.of(CD_Attributed,
083                                                                                                         CD_Collection,
084                                                                                                         CD_Object)),
085                                                             attributesDesc,
086                                                             attributedDesc)));
087  }
088
089  // Used by describeConstable().
090  public static final <T, V> Attributed<T, V> of(final Collection<NamedAttributeMap<V>> attributes, final T attributed) {
091    return new Attributed<>(attributes, attributed);
092  }
093
094}