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_String; 026import static java.lang.constant.DirectMethodHandleDesc.Kind.STATIC; 027 028/** 029 * A {@link Value} whose value is a {@code String}. 030 * 031 * @param value the value 032 * 033 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a> 034 */ 035public final record StringValue(String value) implements Value<StringValue> { 036 037 public static final StringValue EMPTY = new StringValue(""); 038 039 /** 040 * Creates a new {@link StringValue}. 041 * 042 * @param value the value; must not be {@code null} 043 * 044 * @exception NullPointerException if {@code value} is {@code null} 045 */ 046 public StringValue { 047 Objects.requireNonNull(value, "value"); 048 } 049 050 @Override // Comparable<StringValue> 051 public final int compareTo(final StringValue other) { 052 if (other == null) { 053 return -1; 054 } else if (this.equals(other)) { 055 return 0; 056 } 057 return this.value().compareTo(other.value()); 058 } 059 060 @Override // Constable 061 public final Optional<DynamicConstantDesc<StringValue>> describeConstable() { 062 final ClassDesc cd = ClassDesc.of(this.getClass().getName()); 063 return 064 Optional.of(DynamicConstantDesc.of(BSM_INVOKE, 065 MethodHandleDesc.ofMethod(STATIC, 066 cd, 067 "of", 068 MethodTypeDesc.of(cd, 069 CD_String)), 070 this.value())); 071 } 072 073 @Override 074 public final boolean equals(final Object other) { 075 return 076 other == this || 077 // Follow java.lang.annotation.Annotation requirements. 078 other != null && other.getClass() == this.getClass() && this.value().equals(((StringValue)other).value()); 079 } 080 081 public final int hashCode() { 082 // Follow java.lang.annotation.Annotation requirements. 083 return this.value().hashCode(); 084 } 085 086 @Override // Record 087 public final String toString() { 088 return this.value(); 089 } 090 091 /** 092 * Returns a {@link StringValue} suitable for the supplied arguments. 093 * 094 * @param s a non-{@code null} {@code String} 095 * 096 * @return a non-{@code null} {@link StringValue} 097 * 098 * @exception NullPointerException if {@code s} is {@code null} 099 */ 100 public static final StringValue of(final String s) { 101 return s.isEmpty() ? EMPTY : new StringValue(s); 102 } 103 104}