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}