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.construct.element; 015 016import java.lang.constant.ClassDesc; 017import java.lang.constant.Constable; 018import java.lang.constant.DynamicConstantDesc; 019import java.lang.constant.MethodHandleDesc; 020import java.lang.constant.MethodTypeDesc; 021 022import java.util.Objects; 023import java.util.Optional; 024 025import java.util.concurrent.ConcurrentHashMap; 026import java.util.concurrent.ConcurrentMap; 027 028import java.util.stream.IntStream; 029 030import javax.lang.model.element.Name; 031 032import static java.lang.constant.ConstantDescs.BSM_INVOKE; 033import static java.lang.constant.ConstantDescs.CD_String; 034 035import static java.lang.constant.DirectMethodHandleDesc.Kind.STATIC; 036 037import static java.util.Objects.requireNonNull; 038 039/** 040 * A {@link Name} implementation based on {@link String}s. 041 * 042 * <p>This {@link Name} implementation differs from {@link StringName} in that there is no {@link 043 * org.microbean.construct.PrimordialDomain} involved, and therefore no notion of any kind of delegate.</p> 044 * 045 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a> 046 * 047 * @see Name 048 * 049 * @see StringName 050 */ 051public final class SyntheticName implements Constable, Name { 052 053 private static final ConcurrentMap<String, SyntheticName> names = new ConcurrentHashMap<>(); 054 055 private final String value; 056 057 /** 058 * Creates a new {@link SyntheticName}. 059 * 060 * @param value the actual name; must not be {@code null} 061 * 062 * @exception NullPointerException if {@code value} is {@code null} 063 */ 064 private SyntheticName(final String value) { 065 super(); 066 this.value = requireNonNull(value, "value"); 067 } 068 069 @Override // Name (CharSequence) 070 public final char charAt(final int index) { 071 return this.value.charAt(index); 072 } 073 074 @Override // Name (CharSequence) 075 public final IntStream chars() { 076 return this.value.chars(); 077 } 078 079 @Override // Name (CharSequence) 080 public final IntStream codePoints() { 081 return this.value.codePoints(); 082 } 083 084 @Override // Name 085 public final boolean contentEquals(final CharSequence cs) { 086 return this == cs || cs != null && this.value.contentEquals(cs.toString()); 087 } 088 089 @Override // Constable 090 public final Optional<DynamicConstantDesc<SyntheticName>> describeConstable() { 091 return 092 Optional.of(DynamicConstantDesc.of(BSM_INVOKE, 093 MethodHandleDesc.ofMethod(STATIC, 094 ClassDesc.of(this.getClass().getName()), 095 "of", 096 MethodTypeDesc.of(CD_String)), 097 this.value)); 098 } 099 100 @Override // Object 101 public final boolean equals(final Object other) { 102 return this == other || switch (other) { 103 case null -> false; 104 case SyntheticName sn when this.getClass() == sn.getClass() -> Objects.equals(this.value, sn.value); 105 default -> false; 106 }; 107 } 108 109 @Override // Object 110 public final int hashCode() { 111 return this.value.hashCode(); 112 } 113 114 @Override // Name (CharSequence) 115 public final boolean isEmpty() { 116 return this.value.isEmpty(); 117 } 118 119 @Override // Name (CharSequence) 120 public final int length() { 121 return this.value.length(); 122 } 123 124 @Override // Name (CharSequence) 125 public final CharSequence subSequence(final int start, final int end) { 126 return this.value.subSequence(start, end); 127 } 128 129 @Override // Name (CharSequence) 130 public final String toString() { 131 return this.value; 132 } 133 134 /** 135 * Returns a (non-{@code null}) {@link SyntheticName} representing the supplied {@code name}. 136 * 137 * @param name a {@link String}; must not be {@code null} 138 * 139 * @return a {@link SyntheticName}; never {@code null} 140 * 141 * @exception NullPointerException if {@code name} is {@code null} 142 */ 143 public static final SyntheticName of(final String name) { 144 return names.computeIfAbsent(name, SyntheticName::new); 145 } 146 147}