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.attributes; 015 016import java.lang.constant.ClassDesc; 017import java.lang.constant.DynamicConstantDesc; 018import java.lang.constant.MethodHandleDesc; 019import java.lang.constant.MethodTypeDesc; 020 021import java.util.Objects; 022import java.util.Optional; 023 024import static java.lang.constant.ConstantDescs.BSM_INVOKE; 025import static java.lang.constant.ConstantDescs.CD_Class; 026import static java.lang.constant.DirectMethodHandleDesc.Kind.STATIC; 027 028/** 029 * A {@link Value} whose value is a {@code Class}. 030 * 031 * @param <T> the type of the class modeled by this {@link ClassValue} 032 * 033 * @param value the value 034 * 035 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a> 036 */ 037public final record ClassValue<T>(Class<T> value) implements Value<ClassValue<T>> { 038 039 public static final ClassValue<Object> CLASS_JAVA_LANG_OBJECT = new ClassValue<>(Object.class); 040 041 public static final ClassValue<String> CLASS_JAVA_LANG_STRING = new ClassValue<>(String.class); 042 043 /** 044 * Creates a new {@link ClassValue}. 045 * 046 * @param value the value; must not be {@code null} 047 * 048 * @exception NullPointerException if {@code value} is {@code null} 049 */ 050 public ClassValue { 051 Objects.requireNonNull(value, "value"); 052 } 053 054 @Override // Comparable<ClassValue> 055 public final int compareTo(final ClassValue<T> other) { 056 if (other == null) { 057 return -1; 058 } else if (this.equals(other)) { 059 return 0; 060 } 061 return this.value().getName().compareTo(other.value().getName()); 062 } 063 064 @Override // Constable 065 public final Optional<DynamicConstantDesc<ClassValue<T>>> describeConstable() { 066 final ClassDesc cd = ClassDesc.of(this.getClass().getName()); 067 return 068 Optional.of(DynamicConstantDesc.of(BSM_INVOKE, 069 MethodHandleDesc.ofMethod(STATIC, 070 cd, 071 "of", 072 MethodTypeDesc.of(cd, 073 CD_Class)), 074 this.value().describeConstable().orElseThrow())); 075 } 076 077 @Override // Record 078 public final boolean equals(final Object other) { 079 return 080 other == this || 081 // Follow java.lang.annotation.Annotation requirements. 082 other != null && other.getClass() == this.getClass() && this.value().equals(((ClassValue)other).value()); 083 } 084 085 @Override // Record 086 public final int hashCode() { 087 // Follow java.lang.annotation.Annotation requirements. 088 return this.value().hashCode(); 089 } 090 091 @Override // Record 092 public final String toString() { 093 return this.value().getName(); // binary name 094 } 095 096 /** 097 * Returns a {@link ClassValue} suitable for the supplied arguments. 098 * 099 * @param <T> the type of the class 100 * 101 * @param c a {@code Class}; must not be {@code null} 102 * 103 * @return a non-{@code null} {@link ClassValue} 104 * 105 * @exception NullPointerException if {@code c} is {@code null} 106 */ 107 @SuppressWarnings("unchecked") 108 public static final <T> ClassValue<T> of(final Class<T> c) { 109 return 110 c == Object.class ? (ClassValue<T>)CLASS_JAVA_LANG_OBJECT : 111 c == String.class ? (ClassValue<T>)CLASS_JAVA_LANG_STRING : 112 new ClassValue<>(c); 113 } 114 115}