001/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
002 *
003 * Copyright © 2025–2026 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;
015
016import javax.lang.model.element.Name;
017
018import javax.lang.model.type.DeclaredType;
019import javax.lang.model.type.NoType;
020import javax.lang.model.type.NullType;
021import javax.lang.model.type.TypeKind;
022import javax.lang.model.type.TypeMirror;
023
024import org.microbean.construct.element.StringName;
025import org.microbean.construct.element.SyntheticName;
026
027/**
028 * A view of an underlying domain of valid Java constructs that exposes {@linkplain #nullType() the null type}, various
029 * kinds of {@linkplain #noType(TypeKind) pseudo-types}, the {@linkplain #javaLangObjectType() prototypical
030 * <code>java.lang.Object</code> type}, and the ability to {@linkplain #lock() globally lock for symbol completion if
031 * needed}.
032 *
033 * <p>{@link PrimordialDomain}s are primordial because they expose the "first principles" constructs needed to compose
034 * other constructs.</p>
035 *
036 * @author <a href="https://about.me/lairdnelson" target="_top">Laird Nelson</a>
037 *
038 * @see #javaLangObjectType()
039 *
040 * @see #lock()
041 *
042 * @see #noType(TypeKind)
043 *
044 * @see #nullType()
045 *
046 * @see Domain
047 */
048public interface PrimordialDomain {
049
050  /**
051   * Returns the (non-{@code null}, determinate) {@link DeclaredType} representing the <a
052   * href="https://docs.oracle.com/en/java/javase/25/docs/api/java.compiler/javax/lang/model/element/TypeElement.html#prototypicaltype"><dfn>prototypical
053   * type</dfn></a> of {@link Object java.lang.Object}.
054   *
055   * <p>Implementations of this method must not return {@code null}.</p>
056   *
057   * <p>{@link DeclaredType} instances returned by implementations of this method must return {@link TypeKind#DECLARED}
058   * from their {@link TypeMirror#getKind()} method.</p>
059   *
060   * @return the {@link DeclaredType} representing the <dfn>prototypical type</dfn> of {@link Object java.lang.Object};
061   * never {@code null}
062   */
063  public DeclaredType javaLangObjectType();
064
065  /**
066   * Semantically locks an opaque lock used to serialize symbol completion, and returns it in the form of an {@link
067   * Unlockable}.
068   *
069   * <p>Implementations of this method must not return {@code null}.</p>
070   *
071   * @return an {@link Unlockable} in a semantically locked state; never {@code null}
072   *
073   * @see SymbolCompletionLock
074   *
075   * @see Unlockable#close()
076   */
077  public Unlockable lock();
078
079  /**
080   * Returns a (non-{@code null}, determinate) {@link NoType} representing the supplied {@link TypeKind}, provided it is
081   * either {@link TypeKind#NONE} or {@link TypeKind#VOID}.
082   *
083   * <p>Implementations of this method must not return {@code null}.</p>
084   *
085   * @param kind a {@link TypeKind}; must be either {@link TypeKind#NONE} or {@link TypeKind#VOID}
086   *
087   * @return a {@link NoType} representing the supplied {@link TypeKind}; never {@code null}
088   *
089   * @exception NullPointerException if {@code kind} is {@code null}
090   *
091   * @exception IllegalArgumentException if {@code kind} is neither {@link TypeKind#NONE} nor {@link TypeKind#VOID}
092   *
093   * @see javax.lang.model.util.Types#getNoType(TypeKind)
094   */
095  public NoType noType(final TypeKind kind);
096
097  /**
098   * Returns a (non-{@code null}, determinate) {@link NullType} representing the null type.
099   *
100   * <p>Implementations of this method must not return {@code null}.</p>
101   *
102   * @return a {@link NullType} representing the null type; never {@code null}
103   *
104   * @see javax.lang.model.util.Types#getNullType()
105   */
106  public NullType nullType();
107
108  /**
109   * A convenience method that converts the supplied {@link CharSequence}, which is often a {@link Name}, into a {@link
110   * String}, and returns the conversion, {@linkplain #lock() locking} when appropriate to serialize symbol completion.
111   *
112   * <p>The default implementation of this method may return {@code null} if the supplied {@code name} is {@code
113   * null}.</p>
114   *
115   * <p>In many implementations of {@link PrimordialDomain}s, converting a {@link Name} to a {@link String} can cause
116   * problems if symbol completion is taking place concurrently and the symbol completion lock is not held. This method
117   * helps avoid those problems.</p>
118   *
119   * <p>Overriding this method is not normally needed.</p>
120   *
121   * @param name the {@link CharSequence} to convert; may be {@code null} in which case {@code null} will be returned
122   *
123   * @return a {@link String}, or {@code null} if {@code name} was {@code null}
124   *
125   * @see #lock()
126   *
127   * @see Name
128   */
129  @SuppressWarnings("try")
130  public default String toString(final CharSequence name) {
131    return switch (name) {
132    case null -> null;
133    case String s -> s;
134    case StringName sn -> sn.value();
135    case SyntheticName sn -> sn.toString();
136    case Name n -> {
137      try (var lock = this.lock()) {
138        yield n.toString();
139      }
140    }
141    default -> name.toString();
142    };
143  }
144
145}