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}