diff --git a/README.md b/README.md
index 14a6fda..21d9d20 100644
--- a/README.md
+++ b/README.md
@@ -31,7 +31,7 @@ as a Maven dependency:
Always check https://search.maven.org/artifact/org.microbean/microbean-construct
for up-to-date available versions.
-->
- Perhaps surprisingly, the "left hand side" of the putative assignment is represented by the second parameter
+ * ({@code receiver}). The "right hand side" of the putative assignment is represented by the first parameter
+ * ({@code payload}). This follows the contract of the {@link javax.lang.model.util.Types#isAssignable(TypeMirror,
+ * TypeMirror)} method, on which this method is modeled. The number of supplied type arguments must either equal the number of the supplied {@link TypeElement}'s
* {@linkplain TypeElement#getTypeParameters() formal type parameters}, or must be zero. If it is zero, and if the
- * supplied {@link TypeElement} {@link #generic(Element) is generic}, then the supplied {@link TypeElement}'s raw type
- * is returned.
If a parameterized type is returned, {@linkplain DeclaredType#asElement() its TypeElement} must not
* be contained within a {@linkplain #generic(Element) generic} outer class. The parameterized type {@code
@@ -290,6 +345,7 @@ public default DeclaredType declaredType(final CharSequence canonicalName) {
*
* @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html#jls-4.5 Java Language Specification, section 4.5
*/
+ // Type factory method.
public DeclaredType declaredType(final TypeElement typeElement,
final TypeMirror... typeArguments);
@@ -326,6 +382,7 @@ public DeclaredType declaredType(final TypeElement typeElement,
*
* @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html#jls-4.5 Java Language Specification, section 4.5
*/
+ // Type factory method.
public DeclaredType declaredType(final DeclaredType enclosingType,
final TypeElement typeElement,
final TypeMirror... typeArguments);
@@ -334,6 +391,13 @@ public DeclaredType declaredType(final DeclaredType enclosingType,
* Returns a non-{@code null} {@link List} of the direct supertypes of the supplied {@link TypeMirror},
* which is normally a {@linkplain TypeKind#DECLARED declared type}.
*
+ *
The direct supertypes returned by this method are actually a subset of the direct supertypes of a type as
+ * defined in the Java Language
+ * Specification, section 4.10. Specifically, the subset contains only those types that can be expressed in the
+ * {@code extends} or {@code implements} clauses of the Java language. For example, a type {@code Baz} can declare
+ * only that it {@code extends Foo
Implementations of this method must not return {@code null}.
- * - * @return an {@link Unlockable} in a semantically locked state; never {@code null} - * - * @see Unlockable#close() - */ - public Unlockable lock(); + // (Convenience.) + @Override // PrimordialDomain + public default DeclaredType javaLangObjectType() { + return (DeclaredType)this.javaLangObject().asType(); + } /** * Returns a {@link ModuleElement} representing the module {@linkplain ModuleElement#getQualifiedName() named} by the @@ -699,6 +767,7 @@ public default TypeElement javaLangObject() { * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-7.html#jls-7.7 Java Language Specification, section * 7.7 */ + // Element factory method. public ModuleElement moduleElement(final CharSequence qualifiedName); /** @@ -714,6 +783,7 @@ public default TypeElement javaLangObject() { * * @see javax.lang.model.util.Elements#getName(CharSequence) */ + // Element factory method. public Name name(final CharSequence name); /** @@ -735,9 +805,13 @@ public default TypeElement javaLangObject() { * * @see javax.lang.model.util.Types#getNoType(TypeKind) * + * @see NoType + * * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-8.html#jls-8.4.5 Java Language Specification, section * 8.4.5 */ + // Type factory method. + @Override // PrimordialDomain public NoType noType(final TypeKind kind); /** @@ -748,8 +822,12 @@ public default TypeElement javaLangObject() { * * @see javax.lang.model.util.Types#getNullType() * + * @see NullType + * * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html#jls-4.1 Java Language Specification, section 4.1 */ + // Type factory method. + @Override // PrimordialDomain public NullType nullType(); /** @@ -760,6 +838,8 @@ public default TypeElement javaLangObject() { * @return a non-{@code null} {@link Origin} * * @see Elements#getOrigin(Element) + * + * @see Origin */ public Origin origin(final Element e); @@ -777,9 +857,12 @@ public default TypeElement javaLangObject() { * * @see javax.lang.model.util.Elements#getPackageElement(CharSequence) * + * @see PackageElement + * * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-6.html#jls-6.7 Java Language Specification, section * 6.7 */ + // Element factory method. public PackageElement packageElement(final CharSequence canonicalName); /** @@ -799,9 +882,12 @@ public default TypeElement javaLangObject() { * * @see javax.lang.model.util.Elements#getPackageElement(ModuleElement, CharSequence) * + * @see PackageElement + * * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-6.html#jls-6.7 Java Language Specification, section * 6.7 */ + // Element factory method. public PackageElement packageElement(final ModuleElement asSeenFrom, final CharSequence canonicalName); /** @@ -814,7 +900,10 @@ public default TypeElement javaLangObject() { * @exception NullPointerException if {@code gd} is {@code null} * * @exception IllegalArgumentException if {@code gd} is neither a {@link Class} nor an {@link Executable} + * + * @see Parameterizable */ + // (Convenience.) public default Parameterizable parameterizable(final GenericDeclaration gd) { return switch (gd) { case null -> throw new NullPointerException("gd"); @@ -827,16 +916,17 @@ public default Parameterizable parameterizable(final GenericDeclaration gd) { /** * A convenience method that returns {@code true} if and only if {@code t} is a {@link DeclaredType}, {@linkplain * TypeMirror#getKind() has aTypeKind} of {@link TypeKind#DECLARED DECLARED}, and {@linkplain
- * DeclaredType#getTypeArguments() has an empty type arguments list}.
+ * DeclaredType#getTypeArguments() has a non-empty type arguments list}.
*
* @param t a {@link TypeMirror}; must not be {@code null}
*
* @return {@code true} if and only if {@code t} is a {@link DeclaredType}, {@linkplain
* TypeMirror#getKind() has a TypeKind} of {@link TypeKind#DECLARED DECLARED}, and {@linkplain
- * DeclaredType#getTypeArguments() has an empty type arguments list}; {@code false} otherwise
+ * DeclaredType#getTypeArguments() has a non-empty type arguments list}; {@code false} otherwise
*
* @exception NullPointerException if {@code t} is {@code null}
*/
+ // (Convenience.)
public default boolean parameterized(final TypeMirror t) {
return switch (t) {
case null -> throw new NullPointerException("t");
@@ -869,6 +959,8 @@ public default boolean parameterized(final TypeMirror t) {
*
* @see #primitiveType(TypeKind)
*
+ * @see PrimitiveType
+ *
* @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-5.html#jls-5.1.8 Java Language Specification, section
* 5.1.8
*
@@ -910,6 +1002,8 @@ public default PrimitiveType primitiveType(final CharSequence canonicalName) {
*
* @see #primitiveType(TypeMirror)
*
+ * @see PrimitiveType
+ *
* @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-5.html#jls-5.1.8 Java Language Specification, section
* 5.1.8
*/
@@ -934,10 +1028,13 @@ public default PrimitiveType primitiveType(final TypeElement e) {
*
* @see javax.lang.model.util.Types#getPrimitiveType(TypeKind)
*
+ * @see PrimitiveType
+ *
* @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html#jls-4.2 Java Language Specification, section
* 4.2
*/
// (Canonical.)
+ // Type factory method.
public PrimitiveType primitiveType(final TypeKind kind);
/**
@@ -959,6 +1056,7 @@ public default PrimitiveType primitiveType(final TypeElement e) {
*/
// (Canonical.)
// (Unboxing.)
+ // Type factory method.
public PrimitiveType primitiveType(final TypeMirror t);
/**
@@ -971,7 +1069,7 @@ public default PrimitiveType primitiveType(final TypeElement e) {
*
* @param t a {@link TypeMirror}; must not be {@code null}
*
- * @return {@code true} if and only if this {@link UniversalType} represents a prototypical type
+ * @return {@code true} if and only if {@code t} represents a prototypical type
*
* @exception NullPointerException if {@code t} is {@code null}
*
@@ -989,7 +1087,7 @@ public default boolean prototypical(final TypeMirror t) {
}
};
}
-
+
/**
* A convenience method that returns {@code true} if and only if the supplied {@link TypeMirror} is a raw
* type according to the rules
@@ -1005,6 +1103,7 @@ public default boolean prototypical(final TypeMirror t) {
*
* @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html#jls-4.8 Java Language Specification, section 4.8
*/
+ // (Convenience.)
public default boolean raw(final TypeMirror t) {
return switch (t) {
case null -> throw new NullPointerException("t");
@@ -1039,21 +1138,21 @@ yield switch (t.getKind()) {
*
* @exception NullPointerException if {@code t} is {@code null}
*
+ * @see #parameterized(TypeMirror)
+ *
+ * @see #elementType(TypeMirror)
+ *
+ * @see #erasure(TypeMirror)
+ *
* @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html#jls-4.8 Java Language Specification, section
* 4.8
*/
- public default TypeMirror rawType(final TypeMirror t) {
+ // (Convenience.)
+ public default TypeMirror rawType(TypeMirror t) {
return switch (t) {
case null -> throw new NullPointerException("t");
- case UniversalType ut -> ut.rawType();
- default -> {
- try (var lock = this.lock()) {
- yield switch (t.getKind()) {
- case ARRAY -> this.rawType(this.elementType(t)); // recursive
- default -> this.parameterized(t) ? this.erasure(t) : null;
- };
- }
- }
+ case UniversalType ut -> ut.elementType().parameterized() ? this.erasure(ut) : null;
+ default -> this.parameterized(this.elementType(t)) ? this.erasure(t) : null;
};
}
@@ -1145,35 +1244,11 @@ yield switch (t.getKind()) {
*
* @see javax.lang.model.util.Types#isSubtype(TypeMirror, TypeMirror)
*
- * @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html#jls-4.10 Java Language Specification, section
+ * @spec https://docs.oracle.com/javase/specs/jls/se25/html/jls-4.html#jls-4.10 Java Language Specification, section
* 4.10
*/
public boolean subtype(TypeMirror candidateSubtype, TypeMirror supertype);
- /**
- * Converts the supplied {@link CharSequence}, which is often a {@link Name}, into a {@link String}, and returns the
- * conversion, {@linkplain #lock() locking} when appropriate to serialize symbol completion.
- *
- * @param name the {@link CharSequence} to convert; may be {@code null} in which case {@code null} will be returned
- *
- * @return a {@link String}, or {@code null} if {@code name} was {@code null}
- *
- * @see #lock()
- */
- public default String toString(final CharSequence name) {
- return switch (name) {
- case null -> null;
- case String s -> s;
- case StringName sn -> sn.value();
- case Name n -> {
- try (var lock = this.lock()) {
- yield n.toString();
- }
- }
- default -> name.toString();
- };
- }
-
/**
* A convenience method that returns the {@link TypeMirror} corresponding to the supplied (reflective) {@link Type}.
*
@@ -1186,6 +1261,7 @@ public default String toString(final CharSequence name) {
* @exception IllegalArgumentException if {@code t} is not a {@link Class}, {@link GenericArrayType}, {@link
* ParameterizedType}, {@link java.lang.reflect.TypeVariable} or {@link java.lang.reflect.WildcardType}
*/
+ // (Convenience.)
public default TypeMirror type(final Type t) {
// TODO: anywhere there is domain.declaredType(), consider passing
// domain.moduleElement(this.getClass().getModule().getName()) as the first argument. Not sure how this works
@@ -1235,6 +1311,7 @@ public default TypeMirror type(final Type t) {
*
* @see #type(Type)
*/
+ // (Convenience.)
public default TypeMirror[] types(final Type[] ts) {
return switch (ts.length) {
case 0 -> new TypeMirror[0];
@@ -1264,6 +1341,7 @@ public default TypeMirror[] types(final Type[] ts) {
* @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-6.html#jls-6.7 Java Language Specification, section
* 6.7
*/
+ // Element factory method.
public TypeElement typeElement(final CharSequence canonicalName);
/**
@@ -1284,6 +1362,7 @@ public default TypeMirror[] types(final Type[] ts) {
* @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-6.html#jls-6.7 Java Language Specification, section
* 6.7
*/
+ // Element factory method.
public TypeElement typeElement(final ModuleElement asSeenFrom, final CharSequence canonicalName);
/**
@@ -1318,6 +1397,7 @@ public default TypeMirror[] types(final Type[] ts) {
* @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-5.html#jls-5.1.7 Java Language Specification, section
* 5.1.7
*/
+ // Element factory method.
// (Canonical.)
// (Boxing.)
public default TypeElement typeElement(final PrimitiveType t) {
@@ -1380,6 +1460,7 @@ public default TypeElement typeElement(final TypeKind primitiveTypeKind) {
*
* @return a {@link TypeParameterElement}, or {@code null}
*/
+ // (Convenience.)
public default TypeParameterElement typeParameterElement(Parameterizable p, final CharSequence name) {
Objects.requireNonNull(p, "p");
Objects.requireNonNull(name, "name");
@@ -1423,6 +1504,7 @@ public default TypeParameterElement typeParameterElement(Parameterizable p, fina
*
* @see #typeParameterElement(Parameterizable, CharSequence)
*/
+ // (Convenience.)
public default TypeVariable typeVariable(Parameterizable p, final CharSequence name) {
final TypeParameterElement e = this.typeParameterElement(p, name);
return e == null ? null : (TypeVariable)e.asType();
@@ -1450,6 +1532,7 @@ public default TypeVariable typeVariable(Parameterizable p, final CharSequence n
*
* @see VariableElement
*/
+ // (Convenience.)
public default VariableElement variableElement(final Element enclosingElement, final CharSequence simpleName) {
Objects.requireNonNull(simpleName, "simpleName");
return switch (enclosingElement) {
@@ -1489,6 +1572,7 @@ public default VariableElement variableElement(final Element enclosingElement, f
* @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html#jls-4.5.1 Java Language Specification, section
* 4.5.1
*/
+ // (Convenience.)
public default WildcardType wildcardType() {
return this.wildcardType(null, null);
}
@@ -1514,6 +1598,7 @@ public default WildcardType wildcardType() {
* @spec https://docs.oracle.com/javase/specs/jls/se23/html/jls-4.html#jls-4.5.1 Java Language Specification, section
* 4.5.1
*/
+ // Type factory method.
public WildcardType wildcardType(TypeMirror extendsBound, TypeMirror superBound);
}
diff --git a/src/main/java/org/microbean/construct/PrimordialDomain.java b/src/main/java/org/microbean/construct/PrimordialDomain.java
new file mode 100644
index 0000000..eb11a26
--- /dev/null
+++ b/src/main/java/org/microbean/construct/PrimordialDomain.java
@@ -0,0 +1,143 @@
+/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
+ *
+ * Copyright © 2025 microBean™.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package org.microbean.construct;
+
+import javax.lang.model.element.Name;
+
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.NoType;
+import javax.lang.model.type.NullType;
+import javax.lang.model.type.TypeKind;
+import javax.lang.model.type.TypeMirror;
+
+import org.microbean.construct.element.StringName;
+import org.microbean.construct.element.SyntheticName;
+
+/**
+ * A view of an underlying domain of valid Java constructs that exposes {@linkplain #nullType() the null type}, various
+ * kinds of {@linkplain #noType(TypeKind) pseudo-types}, the {@linkplain #javaLangObjectType() prototypical
+ * java.lang.Object type}, and the ability to {@linkplain #lock() globally lock for symbol completion if
+ * needed}.
+ *
+ * {@link PrimordialDomain}s are primordial because they expose the "first principles" constructs needed to compose + * other constructs.
+ * + * @author Laird Nelson + * + * @see #javaLangObjectType() + * + * @see #lock() + * + * @see #noType(TypeKind) + * + * @see #nullType() + */ +public interface PrimordialDomain { + + /** + * Returns the (non-{@code null}, determinate) {@link DeclaredType} representing the prototypical + * type of {@link Object java.lang.Object}. + * + *Implementations of this method must not return {@code null}.
+ * + *{@link DeclaredType} instances returned by implementations of this method must return {@link TypeKind#DECLARED} + * from their {@link TypeMirror#getKind()} method.
+ * + * @return the {@link DeclaredType} representing the prototypical type of {@link Object java.lang.Object}; + * never {@code null} + */ + public DeclaredType javaLangObjectType(); + + /** + * Semantically locks an opaque lock used to serialize symbol completion, and returns it in the form of an {@link + * Unlockable}. + * + *Implementations of this method must not return {@code null}.
+ * + * @return an {@link Unlockable} in a semantically locked state; never {@code null} + * + * @see SymbolCompletionLock + * + * @see Unlockable#close() + */ + public Unlockable lock(); + + /** + * Returns a (non-{@code null}, determinate) {@link NoType} representing the supplied {@link TypeKind}, provided it is + * either {@link TypeKind#NONE} or {@link TypeKind#VOID}. + * + *Implementations of this method must not return {@code null}.
+ * + * @param kind a {@link TypeKind}; must be either {@link TypeKind#NONE} or {@link TypeKind#VOID} + * + * @return a {@link NoType} representing the supplied {@link TypeKind}; never {@code null} + * + * @exception NullPointerException if {@code kind} is {@code null} + * + * @exception IllegalArgumentException if {@code kind} is neither {@link TypeKind#NONE} nor {@link TypeKind#VOID} + * + * @see javax.lang.model.util.Types#getNoType(TypeKind) + */ + public NoType noType(final TypeKind kind); + + /** + * Returns a (non-{@code null}, determinate) {@link NullType} representing the null type. + * + *Implementations of thsi method must not return {@code null}.
+ * + * @return a {@link NullType} representing the null type; never {@code null} + * + * @see javax.lang.model.util.Types#getNullType() + */ + public NullType nullType(); + + /** + * A convenience method that converts the supplied {@link CharSequence}, which is often a {@link Name}, into a {@link + * String}, and returns the conversion, {@linkplain #lock() locking} when appropriate to serialize symbol completion. + * + *The default implementation of this method may return {@code null} if the supplied {@code name} is {@code + * null}.
+ * + *In many implementations of domains, converting a {@link Name} to a {@link String} can cause problems if symbol + * completion is taking place concurrently and the symbol completion lock is not held. This method helps avoid those + * problems.
+ * + *Overriding this method is not normally needed.
+ * + * @param name the {@link CharSequence} to convert; may be {@code null} in which case {@code null} will be returned + * + * @return a {@link String}, or {@code null} if {@code name} was {@code null} + * + * @see #lock() + * + * @see Name + */ + @SuppressWarnings("try") + public default String toString(final CharSequence name) { + return switch (name) { + case null -> null; + case String s -> s; + case StringName sn -> sn.value(); + case SyntheticName sn -> sn.toString(); + case Name n -> { + try (var lock = this.lock()) { + yield n.toString(); + } + } + default -> name.toString(); + }; + } + +} diff --git a/src/main/java/org/microbean/construct/Processor.java b/src/main/java/org/microbean/construct/Processor.java index a9f7a9b..a6dea9f 100644 --- a/src/main/java/org/microbean/construct/Processor.java +++ b/src/main/java/org/microbean/construct/Processor.java @@ -38,8 +38,6 @@ import static java.lang.System.getLogger; -import static java.lang.System.Logger.Level.DEBUG; - final class Processor implements AutoCloseable, javax.annotation.processing.Processor { private static final Logger LOGGER = getLogger(Processor.class.getName()); diff --git a/src/main/java/org/microbean/construct/ReadOnlyModularJavaFileManager.java b/src/main/java/org/microbean/construct/ReadOnlyModularJavaFileManager.java index fa6f2a8..d679c5e 100644 --- a/src/main/java/org/microbean/construct/ReadOnlyModularJavaFileManager.java +++ b/src/main/java/org/microbean/construct/ReadOnlyModularJavaFileManager.java @@ -14,19 +14,13 @@ package org.microbean.construct; import java.io.IOException; -import java.io.InputStream; -import java.io.OutputStream; -import java.io.Reader; import java.io.UncheckedIOException; -import java.io.Writer; import java.lang.module.ModuleReader; import java.lang.module.ModuleReference; import java.lang.System.Logger; -import java.net.URI; - import java.util.ArrayList; import java.util.Collection; import java.util.Collections; @@ -34,7 +28,6 @@ import java.util.List; import java.util.HashMap; import java.util.Map; -import java.util.Objects; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; @@ -42,9 +35,6 @@ import java.util.stream.Collectors; import java.util.stream.Stream; -import javax.lang.model.element.NestingKind; -import javax.lang.model.element.Modifier; - import javax.tools.FileObject; import javax.tools.ForwardingJavaFileManager; import javax.tools.JavaFileManager.Location; diff --git a/src/main/java/org/microbean/construct/RuntimeProcessingEnvironmentSupplier.java b/src/main/java/org/microbean/construct/RuntimeProcessingEnvironmentSupplier.java index c7e60ea..3b15ee2 100644 --- a/src/main/java/org/microbean/construct/RuntimeProcessingEnvironmentSupplier.java +++ b/src/main/java/org/microbean/construct/RuntimeProcessingEnvironmentSupplier.java @@ -15,9 +15,6 @@ import java.lang.System.Logger; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.RunnableFuture; - import java.util.concurrent.atomic.AtomicReference; import java.util.function.Consumer; @@ -83,8 +80,8 @@ private RuntimeProcessingEnvironmentSupplier() { /** - * Closes this {@link RuntimeProcessingEnvironmentSupplier}, which invalidates all {@link ProcessingEnvironment}s - * {@linkplain #get() supplied} by it. + * Closes this {@link RuntimeProcessingEnvironmentSupplier}, which invalidates all {@link + * ProcessingEnvironment}s {@linkplain #get() supplied} by it. * *A subsequent call to {@link #get()} will reset this {@link RuntimeProcessingEnvironmentSupplier}.
* @@ -103,7 +100,8 @@ public final void close() { /** * Returns a non-{@code null}, {@link ProcessingEnvironment} suitable for runtime use. * - *{@link ProcessingEnvironment} instances are not guaranteed to be thread-safe.
+ *{@link ProcessingEnvironment} instances are not guaranteed to be safe for concurrent use by multiple + * threads.
* * @return a non-{@code null} {@link ProcessingEnvironment} * diff --git a/src/main/java/org/microbean/construct/UniversalConstruct.java b/src/main/java/org/microbean/construct/UniversalConstruct.java index 8bb95ff..49d6f46 100644 --- a/src/main/java/org/microbean/construct/UniversalConstruct.java +++ b/src/main/java/org/microbean/construct/UniversalConstruct.java @@ -19,28 +19,36 @@ import java.lang.constant.Constable; import java.lang.constant.ConstantDesc; import java.lang.constant.DynamicConstantDesc; -import java.lang.constant.MethodHandleDesc; +import java.util.ArrayList; import java.util.List; -import java.util.Objects; import java.util.Optional; import java.util.function.Supplier; import javax.lang.model.AnnotatedConstruct; +import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.Element; import javax.lang.model.type.TypeMirror; import org.microbean.construct.constant.Constables; +import org.microbean.construct.element.SyntheticAnnotationMirror; import org.microbean.construct.element.UniversalAnnotation; import org.microbean.construct.element.UniversalElement; import org.microbean.construct.type.UniversalType; import static java.lang.constant.ConstantDescs.BSM_INVOKE; +import static java.lang.constant.ConstantDescs.CD_List; + +import static java.lang.constant.MethodHandleDesc.ofConstructor; + +import static java.util.Collections.unmodifiableList; + +import static java.util.Objects.requireNonNull; /** * An abstract implementation of {@link AnnotatedConstruct} from which only {@link UniversalElement} and {@link @@ -48,6 +56,8 @@ * * @paramThis {@link Name} implementation differs from {@link SyntheticName} in that it involves usage of the {@link + * PrimordialDomain#toString(Name)} method, which gives a {@link PrimordialDomain} implementation a chance to cache the + * underlying resulting {@link Name}.
+ * * @param value the actual name; must not be {@code null} * - * @param domain a {@link Domain}; must not be {@code null} + * @param domain a {@link PrimordialDomain}; must not be {@code null} * * @author Laird Nelson * * @see Name * - * @see Domain#toString(CharSequence) + * @see SyntheticName + * + * @see PrimordialDomain#toString(CharSequence) */ -public final record StringName(String value, Domain domain) implements Constable, Name { +public final record StringName(String value, PrimordialDomain domain) implements Constable, Name { /** * Creates a new {@link StringName}. * * @param value the actual name; must not be {@code null} * - * @param domain a {@link Domain}; must not be {@code null} + * @param domain a {@link PrimordialDomain}; must not be {@code null} * * @exception NullPointerException if either argument is {@code null} */ - public StringName(final CharSequence value, final Domain domain) { - // We deliberately route even String-typed values through Domain#toString(CharSequence) in case the Domain wishes to + public StringName(final CharSequence value, final PrimordialDomain domain) { + // We deliberately route even String-typed values through PrimordialDomain#toString(CharSequence) in case the PrimordialDomain wishes to // cache the intermediate Name. this(switch (value) { case StringName sn -> sn.value(); @@ -68,7 +74,7 @@ public StringName(final CharSequence value, final Domain domain) { * * @param value the actual name; must not be {@code null} * - * @param domain a {@link Domain}; must not be {@code null} + * @param domain a {@link PrimordialDomain}; must not be {@code null} * * @exception NullPointerException if either argument is {@code null} */ @@ -110,7 +116,7 @@ public final Optional extends ConstantDesc> describeConstable() { .map(domainDesc -> DynamicConstantDesc.of(BSM_INVOKE, MethodHandleDesc.ofConstructor(ClassDesc.of(this.getClass().getName()), ClassDesc.of(CharSequence.class.getName()), - ClassDesc.of(Domain.class.getName())), + ClassDesc.of(PrimordialDomain.class.getName())), this.value(), domainDesc)); } @@ -157,21 +163,21 @@ public final String toString() { /** * Returns a {@link StringName} whose {@link #value()} method will return a {@link String} {@linkplain - * String#equals(Object) equal to} the {@linkplain Domain#toString(CharSequence)String conversion of}
- * the supplied {@link CharSequence}, and whose {@link #domain()} method will return a {@link Domain} {@linkplain
- * #equals(Object) equal to} the supplied {@link Domain}.
+ * String#equals(Object) equal to} the {@linkplain PrimordialDomain#toString(CharSequence) String conversion of}
+ * the supplied {@link CharSequence}, and whose {@link #domain()} method will return a {@link PrimordialDomain} {@linkplain
+ * #equals(Object) equal to} the supplied {@link PrimordialDomain}.
*
* @param cs a {@link CharSequence}; must not be {@code null}
*
- * @param domain a {@link Domain}; must not be {@code null}
+ * @param domain a {@link PrimordialDomain}; must not be {@code null}
*
* @return a {@link StringName}; never {@code null}
*
* @exception NullPointerException if either argument is {@code null}
*
- * @see Domain#toString(CharSequence)
+ * @see PrimordialDomain#toString(CharSequence)
*/
- public static final StringName of(final CharSequence cs, final Domain domain) {
+ public static final StringName of(final CharSequence cs, final PrimordialDomain domain) {
return cs instanceof StringName sn ? sn : new StringName(domain.toString(cs), domain);
}
diff --git a/src/main/java/org/microbean/construct/element/SyntheticAnnotationMirror.java b/src/main/java/org/microbean/construct/element/SyntheticAnnotationMirror.java
new file mode 100644
index 0000000..e0fc4a3
--- /dev/null
+++ b/src/main/java/org/microbean/construct/element/SyntheticAnnotationMirror.java
@@ -0,0 +1,292 @@
+/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*-
+ *
+ * Copyright © 2025 microBean™.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
+ * an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations under the License.
+ */
+package org.microbean.construct.element;
+
+import java.lang.annotation.Annotation;
+
+import java.lang.constant.ClassDesc;
+import java.lang.constant.Constable;
+import java.lang.constant.ConstantDesc;
+import java.lang.constant.DynamicConstantDesc;
+import java.lang.constant.MethodTypeDesc;
+
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+import java.util.Map.Entry;
+import java.util.Optional;
+
+import java.util.function.Function;
+
+import javax.lang.model.element.AnnotationMirror;
+import javax.lang.model.element.AnnotationValue;
+import javax.lang.model.element.AnnotationValueVisitor;
+import javax.lang.model.element.Element;
+import javax.lang.model.element.ExecutableElement;
+import javax.lang.model.element.TypeElement;
+import javax.lang.model.element.VariableElement;
+
+import javax.lang.model.type.DeclaredType;
+import javax.lang.model.type.TypeMirror;
+
+import org.microbean.construct.constant.Constables;
+
+import static java.lang.constant.ConstantDescs.BSM_INVOKE;
+import static java.lang.constant.ConstantDescs.CD_Map;
+import static java.lang.constant.ConstantDescs.CD_Object;
+import static java.lang.constant.ConstantDescs.NULL;
+
+import static java.lang.constant.DirectMethodHandleDesc.Kind.INTERFACE_STATIC;
+
+import static java.lang.constant.MethodHandleDesc.ofConstructor;
+
+import static java.util.Arrays.fill;
+
+import static java.util.Collections.unmodifiableMap;
+
+import static java.util.LinkedHashMap.newLinkedHashMap;
+
+import static java.util.Objects.requireNonNull;
+
+import static javax.lang.model.element.ElementKind.ANNOTATION_TYPE;
+import static javax.lang.model.element.ElementKind.METHOD;
+
+/**
+ * An experimental {@link AnnotationMirror} implementation that is partially or wholly synthetic.
+ *
+ * @author Laird Nelson
+ */
+public final class SyntheticAnnotationMirror implements AnnotationMirror, Constable {
+
+ private final TypeElement annotationTypeElement;
+
+ private final MapThis {@link Name} implementation differs from {@link StringName} in that there is no {@link + * org.microbean.construct.PrimordialDomain} involved, and therefore no notion of any kind of delegate.
+ * + * @author Laird Nelson + * + * @see Name + * + * @see StringName + */ +public final class SyntheticName implements Constable, Name { + + private static final ConcurrentMapThe definition of type sameness appears in the contract of the {@link Domain#sameType(TypeMirror, - * TypeMirror)} method, which, in turn, relies on the contract of the {@link - * javax.lang.model.util.Types#isSameType(TypeMirror, TypeMirror)} method.
- * - * @param t a {@link TypeMirror}; may be {@code null} in which case {@code false} will be returned - * - * @return {@code true} if and only if this {@link UniversalType} is the same type as the supplied {@link - * TypeMirror} - * - * @see Domain#sameType(TypeMirror, TypeMirror) - * - * @see javax.lang.model.util.Types#isSameType(TypeMirror, TypeMirror) - * - * @see #equals(Object) - * - * @see TypeMirror#equals(Object) - */ - public final boolean sameType(final TypeMirror t) { - return this.domain().sameType(this, t); - } - private final List extends UniversalType> wrap(final Collection extends TypeMirror> ts) { return of(ts, this.domain()); } @@ -419,13 +387,13 @@ private final UniversalType wrap(final TypeMirror t) { * * @param ts a {@link Collection} of {@link TypeMirror}s; must not be {@code null} * - * @param domain a {@link Domain}; must not be {@code null} + * @param domain a {@link PrimordialDomain}; must not be {@code null} * * @return a non-{@code null}, immutable {@link List} of {@link UniversalType}s * * @exception NullPointerException if either argument is {@code null} */ - public static final List extends UniversalType> of(final Collection extends TypeMirror> ts, final Domain domain) { + public static final List extends UniversalType> of(final Collection extends TypeMirror> ts, final PrimordialDomain domain) { if (ts.isEmpty()) { return List.of(); } @@ -442,15 +410,15 @@ public static final List extends UniversalType> of(final Collection extends * * @param t a {@link TypeMirror}; may be {@code null} in which case {@code null} will be returned * - * @param domain a {@link Domain}; must not be {@code null} + * @param domain a {@link PrimordialDomain}; must not be {@code null} * * @return a {@link UniversalType}, or {@code null} (if {@code t} is {@code null}) * * @exception NullPointerException if {@code domain} is {@code null} * - * @see #UniversalType(TypeMirror, Domain) + * @see #UniversalType(TypeMirror, PrimordialDomain) */ - public static final UniversalType of(final TypeMirror t, final Domain domain) { + public static final UniversalType of(final TypeMirror t, final PrimordialDomain domain) { return switch (t) { case null -> null; case UniversalType ut -> ut; diff --git a/src/test/java/org/microbean/construct/TestDefaultDomain.java b/src/test/java/org/microbean/construct/TestDefaultDomain.java index 9670ce2..3fe91c5 100644 --- a/src/test/java/org/microbean/construct/TestDefaultDomain.java +++ b/src/test/java/org/microbean/construct/TestDefaultDomain.java @@ -1,6 +1,6 @@ /* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- * - * Copyright © 2024 microBean™. + * Copyright © 2024–2025 microBean™. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with * the License. You may obtain a copy of the License at @@ -13,11 +13,16 @@ */ package org.microbean.construct; +import java.lang.annotation.Inherited; + import java.util.List; +import javax.lang.model.element.AnnotationMirror; import javax.lang.model.element.ElementKind; import javax.lang.model.element.Name; +import javax.lang.model.element.TypeElement; +import javax.lang.model.type.DeclaredType; import javax.lang.model.type.PrimitiveType; import javax.lang.model.type.TypeKind; @@ -25,6 +30,7 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNotSame; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertThrows; @@ -182,4 +188,26 @@ final void testSameTypes() { assertTrue(domain.sameType(t0, t1)); } + @Test + final void testAllAnnotations0() { + final TypeElement e0 = domain.typeElement("java.lang.annotation.Documented"); + final List extends AnnotationMirror> as = domain.allAnnotationMirrors(e0); + assertEquals(3, as.size()); // @Documented, @Retention, @Target + } + + @Test + final void testAllAnnotation1() { + final TypeElement e0 = domain.typeElement(Bottom.class.getCanonicalName()); + final List extends AnnotationMirror> as = domain.allAnnotationMirrors(e0); + assertEquals(1, as.size()); // @InheritMe + } + + @Inherited + @interface InheritMe {} + + @InheritMe + private static class Top {} + + private static class Bottom extends Top {} + }