From fac19cd53d25cbb97acd6a67fad19fcab4c51247 Mon Sep 17 00:00:00 2001 From: Laird Nelson Date: Sun, 5 Jan 2025 14:54:18 -0800 Subject: [PATCH] Edit 8 (#15) * Refactoring Signed-off-by: Laird Nelson * Refactoring Signed-off-by: Laird Nelson * Refactoring Signed-off-by: Laird Nelson * Refactoring mainly in the area of events Signed-off-by: Laird Nelson * Massive road grading Signed-off-by: Laird Nelson --------- Signed-off-by: Laird Nelson --- .../mvn-release-prepare-perform.yaml | 10 +- .github/workflows/mvn-verify.yaml | 8 +- .mvn/wrapper/maven-wrapper.properties | 20 + NOTES.md | 71 -- README.md | 2 +- TRACES.txt | 909 ------------------ mvnw | 259 +++++ mvnw.cmd | 149 +++ pom.xml | 447 +++++++-- src/main/java/module-info.java | 3 +- .../microbean/bean/AbstractTypeMatcher.java | 427 ++++++++ .../org/microbean/bean/AttributedElement.java | 4 +- .../org/microbean/bean/AttributedType.java | 4 +- .../microbean/bean/AutoCloseableRegistry.java | 20 +- .../microbean/bean/BeanQualifiersMatcher.java | 53 + .../org/microbean/bean/BeanTypeMatcher.java | 543 +++++++++++ .../java/org/microbean/bean/BeanTypes.java | 313 ++---- .../org/microbean/bean/ConstantDescs.java | 4 +- .../java/org/microbean/bean/Creation.java | 6 +- .../bean/DefaultAutoCloseableRegistry.java | 20 +- .../microbean/bean/DisposableReference.java | 6 +- ...tcher.java => EventQualifiersMatcher.java} | 34 +- .../org/microbean/bean/EventTypeMatcher.java | 231 +++++ .../java/org/microbean/bean/EventTypes.java | 159 +++ src/main/java/org/microbean/bean/Factory.java | 8 +- src/main/java/org/microbean/bean/Id.java | 57 +- .../java/org/microbean/bean/IdMatcher.java | 8 +- .../java/org/microbean/bean/Initializer.java | 2 +- .../java/org/microbean/bean/Producer.java | 11 +- src/main/java/org/microbean/bean/Ranked.java | 6 +- .../org/microbean/bean/RankedReducer.java | 8 +- src/main/java/org/microbean/bean/Request.java | 8 +- .../java/org/microbean/bean/TypeMatcher.java | 900 ----------------- src/main/java/org/microbean/bean/Types.java | 264 +++++ .../java/org/microbean/bean/model/Trees.java | 10 +- src/site/markdown/factories.md | 50 - src/site/markdown/stacks.md | 651 ------------- src/site/site.xml | 27 +- src/test/java/logging.properties | 2 +- .../bean/ExperimentalBeforeAllCallback.java | 31 + .../bean/ExperimentalBeforeEachCallback.java | 53 + .../ExperimentalPreConstructCallback.java | 32 + ...ExperimentalTestInstancePostProcessor.java | 30 + .../org/microbean/bean/TestAssignability.java | 111 +++ .../java/org/microbean/bean/TestBean.java | 17 +- .../org/microbean/bean/TestBeanSelection.java | 57 +- .../org/microbean/bean/TestBeanTypes.java | 137 ++- .../bean/TestConstableSemantics.java | 32 +- .../java/org/microbean/bean/TestContains.java | 118 +++ .../java/org/microbean/bean/TestFactory.java | 2 +- .../bean/TestJUnitExtensionsPerClass.java | 46 + .../bean/TestJUnitExtensionsPerMethod.java | 46 + .../microbean/bean/TestWeldEventDelivery.java | 122 +++ .../microbean/bean/model/TestElements.java | 15 +- 54 files changed, 3400 insertions(+), 3163 deletions(-) create mode 100644 .mvn/wrapper/maven-wrapper.properties delete mode 100644 NOTES.md delete mode 100644 TRACES.txt create mode 100755 mvnw create mode 100644 mvnw.cmd create mode 100644 src/main/java/org/microbean/bean/AbstractTypeMatcher.java create mode 100644 src/main/java/org/microbean/bean/BeanQualifiersMatcher.java create mode 100644 src/main/java/org/microbean/bean/BeanTypeMatcher.java rename src/main/java/org/microbean/bean/{QualifiersMatcher.java => EventQualifiersMatcher.java} (52%) create mode 100644 src/main/java/org/microbean/bean/EventTypeMatcher.java create mode 100644 src/main/java/org/microbean/bean/EventTypes.java delete mode 100644 src/main/java/org/microbean/bean/TypeMatcher.java create mode 100644 src/main/java/org/microbean/bean/Types.java delete mode 100644 src/site/markdown/factories.md delete mode 100644 src/site/markdown/stacks.md create mode 100644 src/test/java/org/microbean/bean/ExperimentalBeforeAllCallback.java create mode 100644 src/test/java/org/microbean/bean/ExperimentalBeforeEachCallback.java create mode 100644 src/test/java/org/microbean/bean/ExperimentalPreConstructCallback.java create mode 100644 src/test/java/org/microbean/bean/ExperimentalTestInstancePostProcessor.java create mode 100644 src/test/java/org/microbean/bean/TestAssignability.java create mode 100644 src/test/java/org/microbean/bean/TestContains.java create mode 100644 src/test/java/org/microbean/bean/TestJUnitExtensionsPerClass.java create mode 100644 src/test/java/org/microbean/bean/TestJUnitExtensionsPerMethod.java create mode 100644 src/test/java/org/microbean/bean/TestWeldEventDelivery.java diff --git a/.github/workflows/mvn-release-prepare-perform.yaml b/.github/workflows/mvn-release-prepare-perform.yaml index a93af95..c858555 100644 --- a/.github/workflows/mvn-release-prepare-perform.yaml +++ b/.github/workflows/mvn-release-prepare-perform.yaml @@ -30,14 +30,14 @@ jobs: persist-credentials: false - id: 'setup-java' name: 'Step: Set Up Java and Maven' - uses: 'actions/setup-java@v3' + uses: 'actions/setup-java@v4' with: cache: 'maven' distribution: 'temurin' gpg-passphrase: 'GPG_PASSPHRASE' gpg-private-key: '${{ secrets.GPG_PRIVATE_KEY }}' - java-version: '21' - mvn-toolchain-id: 'Temurin 21' + java-version: '23' + mvn-toolchain-id: 'Temurin 23' mvn-toolchain-vendor: 'openjdk' # see ../../pom.xml server-id: 'sonatype-oss-repository-hosting' # see https://github.com/microbean/microbean-parent/blob/master/pom.xml#L38 server-password: 'SONATYPE_OSSRH_PASSWORD' @@ -76,7 +76,7 @@ jobs: echo "::group::Running mvn prepare" - mvn --batch-mode ${MVN_DEBUG} --errors ${MVN_TRANSFER_LOGGING} release:prepare + ./mvnw --batch-mode ${MVN_DEBUG} --errors ${MVN_TRANSFER_LOGGING} release:prepare -DdryRun="${DRY_RUN}" -Darguments="${MVN_TRANSFER_LOGGING}" -Dscm.url="${SCM_GIT_HTTPS_URL}" @@ -94,7 +94,7 @@ jobs: set +e { - mvn --batch-mode ${MVN_DEBUG} --errors ${MVN_TRANSFER_LOGGING} release:perform + ./mvnw --batch-mode ${MVN_DEBUG} --errors ${MVN_TRANSFER_LOGGING} release:perform -Darguments="${MVN_TRANSFER_LOGGING} -Dscmpublish.dryRun=${DRY_RUN} -Dscmpublish.pubScmUrl=${SCM_GIT_HTTPS_URL} -DskipTests -DstagingProfileId=${SONATYPE_OSSRH_STAGING_PROFILE_ID}" -DdryRun="${DRY_RUN}" -Dgoals="process-classes,post-site,scm-publish:publish-scm,deploy" diff --git a/.github/workflows/mvn-verify.yaml b/.github/workflows/mvn-verify.yaml index 37c49e3..a414901 100644 --- a/.github/workflows/mvn-verify.yaml +++ b/.github/workflows/mvn-verify.yaml @@ -18,13 +18,13 @@ jobs: persist-credentials: false - id: 'setup-java' name: 'Step: Set Up Java and Maven' - uses: 'actions/setup-java@v3' + uses: 'actions/setup-java@v4' with: cache: 'maven' distribution: 'temurin' - java-version: '21' - mvn-toolchain-id: 'Temurin 21' + java-version: '23' + mvn-toolchain-id: 'Temurin 23' mvn-toolchain-vendor: 'openjdk' # see ../../pom.xml - id: 'mvn-verify' name: 'Step: Maven Verify' - run: 'mvn --batch-mode --color never --errors --no-transfer-progress -Dorg.slf4j.simpleLogger.defaultLogLevel=info verify' + run: './mvnw --batch-mode --color never --errors --no-transfer-progress -Dorg.slf4j.simpleLogger.defaultLogLevel=info verify' diff --git a/.mvn/wrapper/maven-wrapper.properties b/.mvn/wrapper/maven-wrapper.properties new file mode 100644 index 0000000..bca10a6 --- /dev/null +++ b/.mvn/wrapper/maven-wrapper.properties @@ -0,0 +1,20 @@ +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +wrapperVersion=3.3.2 +distributionType=only-script +distributionUrl=https://repo.maven.apache.org/maven2/org/apache/maven/apache-maven/3.9.9/apache-maven-3.9.9-bin.zip + diff --git a/NOTES.md b/NOTES.md deleted file mode 100644 index 7fd67b9..0000000 --- a/NOTES.md +++ /dev/null @@ -1,71 +0,0 @@ -# Notes - -## Model - -For every `Factory`: -* Grab its dependencies -* Ask a `Selectable` to - -## Obsolete, or Unmaintained - -### Factories, Producers, etc. - -A `Factory` is the user-visible creation mechanism. - -It is usually comprised of: - -* A `Producer`. This creates the product, in a possibly intercepted manner, resolving its creational dependencies as - necessary. - * Because creating the product may be intercepted, you can break this down conceptually into: - * Dependency resolution. Something has to get the initial parameters for whatever the means of production is. - * These will either be directly supplied to the means of production or via a type conversion machine (e.g. `Object[]`) - to the construction interceptor machinery - * Instantiation. The thing that actually does the instantiation in the absence of construction - - - - -``` -Producer: - -produce(); - -Intercepting producer: - -public class InterceptingProducer { - private final Chain c; - public InterceptingProducer(Chain c) { - super(); - this.c = c; - } - @Override - public Object produce() { - return this.c.call(); - } -} -``` - -Instead of taking a `Chain` it should take the raw materials to make a chain that sets its target: - -* A `List` of `InterceptorMethod`s -* A "terminal function" (`Function`) -* A supplier of arguments (`Supplier`) - -This also performs constructor interception. - -** We can break this down further into an "instantiator", which comes up with construction arguments via dependency - resolution, and an interceptor that intercepts these arguments and - - -** This _may_ (haven't decided yet) decompose into something that does the resolution, and the "instantiator", whose job - is simply to `new`. -* An `Initializer`. This calls the product's initializer methods. -* A `PostInitializer`. This calls the product's `postConstruct()` callbacks. -* A `PreDestructor`. This calls the product's `preDestroy()` callbacks. - -### `AutoCloseableRegistry` Instances - -An `AutoCloseableRegistry` is an `AutoCloseable` and a collection of `AutoCloseable`s. Therefore it is also a tree. - -All such trees in the system descend from a primordial root. There is therefore only one tree, not many. - diff --git a/README.md b/README.md index f8b0324..4659c79 100644 --- a/README.md +++ b/README.md @@ -27,7 +27,7 @@ dependency: org.microbean microbean-bean - 0.0.10 + 0.0.11 ``` diff --git a/TRACES.txt b/TRACES.txt deleted file mode 100644 index 8351b32..0000000 --- a/TRACES.txt +++ /dev/null @@ -1,909 +0,0 @@ -java.lang.Exception: Stack trace - at java.base/java.lang.Thread.dumpStack(Thread.java:2246) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang.declaredType(Lang.java:1373) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang$ConstableTypeAndElementSource.declaredType(Lang.java:2016) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.TypeAndElementSource.declaredType(TypeAndElementSource.java:56) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.bean(DefaultBeanSet.java:221) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:177) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:137) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:109) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.TestDefaultBeanSet.setup(TestDefaultBeanSet.java:74) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) - at java.base/java.lang.reflect.Method.invoke(Method.java:578) - at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727) - at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) - at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptLifecycleMethod(TimeoutExtension.java:128) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptBeforeEachMethod(TimeoutExtension.java:78) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeMethodInExtensionContext(ClassBasedTestDescriptor.java:520) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$synthesizeBeforeEachMethodAdapter$23(ClassBasedTestDescriptor.java:505) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeEachMethods$3(TestMethodTestDescriptor.java:174) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeMethodsOrCallbacksUntilExceptionOccurs$6(TestMethodTestDescriptor.java:202) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeMethodsOrCallbacksUntilExceptionOccurs(TestMethodTestDescriptor.java:202) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeEachMethods(TestMethodTestDescriptor.java:171) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:134) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) - at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) - at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) - at java.base/java.util.concurrent.ForkJoinPool.helpJoin(ForkJoinPool.java:2076) - at java.base/java.util.concurrent.ForkJoinTask.awaitDone(ForkJoinTask.java:423) - at java.base/java.util.concurrent.ForkJoinTask.join(ForkJoinTask.java:651) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.joinConcurrentTasksInReverseOrderToEnableWorkStealing(ForkJoinPoolHierarchicalTestExecutorService.java:179) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.invokeAll(ForkJoinPoolHierarchicalTestExecutorService.java:153) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) - at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) - at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) - at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312) - at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843) - at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808) - at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188) -java.lang.Exception: Stack trace - at java.base/java.lang.Thread.dumpStack(Thread.java:2246) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang.declaredType(Lang.java:1373) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang$ConstableTypeAndElementSource.declaredType(Lang.java:2016) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.TypeAndElementSource.declaredType(TypeAndElementSource.java:56) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.bean(DefaultBeanSet.java:221) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:177) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:137) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:109) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.TestDefaultBeanSet.setup(TestDefaultBeanSet.java:74) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) - at java.base/java.lang.reflect.Method.invoke(Method.java:578) - at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727) - at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) - at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptLifecycleMethod(TimeoutExtension.java:128) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptBeforeEachMethod(TimeoutExtension.java:78) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeMethodInExtensionContext(ClassBasedTestDescriptor.java:520) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$synthesizeBeforeEachMethodAdapter$23(ClassBasedTestDescriptor.java:505) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeEachMethods$3(TestMethodTestDescriptor.java:174) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeMethodsOrCallbacksUntilExceptionOccurs$6(TestMethodTestDescriptor.java:202) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeMethodsOrCallbacksUntilExceptionOccurs(TestMethodTestDescriptor.java:202) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeEachMethods(TestMethodTestDescriptor.java:171) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:134) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) - at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) - at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) - at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.tryRemoveAndExec(ForkJoinPool.java:1351) - at java.base/java.util.concurrent.ForkJoinTask.awaitDone(ForkJoinTask.java:422) - at java.base/java.util.concurrent.ForkJoinTask.join(ForkJoinTask.java:651) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.joinConcurrentTasksInReverseOrderToEnableWorkStealing(ForkJoinPoolHierarchicalTestExecutorService.java:179) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.invokeAll(ForkJoinPoolHierarchicalTestExecutorService.java:153) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) - at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) - at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) - at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312) - at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843) - at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808) - at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188) -java.lang.Exception: Stack trace -[INFO] Tests run: 8, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.228 s - in org.microbean.bean.TestBean - at java.base/java.lang.Thread.dumpStack(Thread.java:2246) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang.declaredType(Lang.java:1373) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang$ConstableTypeAndElementSource.declaredType(Lang.java:2016) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.TypeAndElementSource.declaredType(TypeAndElementSource.java:56) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.bean(DefaultBeanSet.java:223) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:177) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:137) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:109) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.TestDefaultBeanSet.setup(TestDefaultBeanSet.java:74) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) - at java.base/java.lang.reflect.Method.invoke(Method.java:578) - at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727) - at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) - at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptLifecycleMethod(TimeoutExtension.java:128) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptBeforeEachMethod(TimeoutExtension.java:78) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeMethodInExtensionContext(ClassBasedTestDescriptor.java:520) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$synthesizeBeforeEachMethodAdapter$23(ClassBasedTestDescriptor.java:505) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeEachMethods$3(TestMethodTestDescriptor.java:174) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeMethodsOrCallbacksUntilExceptionOccurs$6(TestMethodTestDescriptor.java:202) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeMethodsOrCallbacksUntilExceptionOccurs(TestMethodTestDescriptor.java:202) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeEachMethods(TestMethodTestDescriptor.java:171) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:134) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) - at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) - at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) - at java.base/java.util.concurrent.ForkJoinPool.helpJoin(ForkJoinPool.java:2076) - at java.base/java.util.concurrent.ForkJoinTask.awaitDone(ForkJoinTask.java:423) - at java.base/java.util.concurrent.ForkJoinTask.join(ForkJoinTask.java:651) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.joinConcurrentTasksInReverseOrderToEnableWorkStealing(ForkJoinPoolHierarchicalTestExecutorService.java:179) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.invokeAll(ForkJoinPoolHierarchicalTestExecutorService.java:153) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) - at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) - at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) - at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312) - at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843) - at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808) - at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188) -java.lang.Exception: Stack trace - at java.base/java.lang.Thread.dumpStack(Thread.java:2246) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang.declaredType(Lang.java:1373) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang$ConstableTypeAndElementSource.declaredType(Lang.java:2016) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.TypeAndElementSource.declaredType(TypeAndElementSource.java:56) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.bean(DefaultBeanSet.java:223) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:177) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:137) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:109) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.TestDefaultBeanSet.setup(TestDefaultBeanSet.java:74) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) - at java.base/java.lang.reflect.Method.invoke(Method.java:578) - at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727) - at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) - at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptLifecycleMethod(TimeoutExtension.java:128) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptBeforeEachMethod(TimeoutExtension.java:78) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeMethodInExtensionContext(ClassBasedTestDescriptor.java:520) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$synthesizeBeforeEachMethodAdapter$23(ClassBasedTestDescriptor.java:505) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeEachMethods$3(TestMethodTestDescriptor.java:174) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeMethodsOrCallbacksUntilExceptionOccurs$6(TestMethodTestDescriptor.java:202) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeMethodsOrCallbacksUntilExceptionOccurs(TestMethodTestDescriptor.java:202) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeEachMethods(TestMethodTestDescriptor.java:171) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:134) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) - at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) - at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) - at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.tryRemoveAndExec(ForkJoinPool.java:1351) - at java.base/java.util.concurrent.ForkJoinTask.awaitDone(ForkJoinTask.java:422) - at java.base/java.util.concurrent.ForkJoinTask.join(ForkJoinTask.java:651) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.joinConcurrentTasksInReverseOrderToEnableWorkStealing(ForkJoinPoolHierarchicalTestExecutorService.java:179) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.invokeAll(ForkJoinPoolHierarchicalTestExecutorService.java:153) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) - at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) - at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) - at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312) - at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843) - at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808) - at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188) - -java.lang.NullPointerException: Cannot invoke "javax.lang.model.type.TypeMirror.toString()" because "t" is null - at jdk.compiler/com.sun.tools.javac.model.JavacTypes.getDeclaredType0(JavacTypes.java:272) - at jdk.compiler/com.sun.tools.javac.model.JavacTypes.getDeclaredType(JavacTypes.java:241) - at jdk.compiler/com.sun.tools.javac.model.JavacTypes.getDeclaredType(JavacTypes.java:249) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang.declaredType(Lang.java:1387) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang$ConstableTypeAndElementSource.declaredType(Lang.java:2016) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.bean(DefaultBeanSet.java:223) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:177) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:137) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:109) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.TestDefaultBeanSet.setup(TestDefaultBeanSet.java:74) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) - at java.base/java.lang.reflect.Method.invoke(Method.java:578) - at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727) - at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) - at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptLifecycleMethod(TimeoutExtension.java:128) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptBeforeEachMethod(TimeoutExtension.java:78) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeMethodInExtensionContext(ClassBasedTestDescriptor.java:520) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$synthesizeBeforeEachMethodAdapter$23(ClassBasedTestDescriptor.java:505) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeEachMethods$3(TestMethodTestDescriptor.java:174) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeMethodsOrCallbacksUntilExceptionOccurs$6(TestMethodTestDescriptor.java:202) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeMethodsOrCallbacksUntilExceptionOccurs(TestMethodTestDescriptor.java:202) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeEachMethods(TestMethodTestDescriptor.java:171) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:134) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) - at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) - at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) - at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.tryRemoveAndExec(ForkJoinPool.java:1351) - at java.base/java.util.concurrent.ForkJoinTask.awaitDone(ForkJoinTask.java:422) - at java.base/java.util.concurrent.ForkJoinTask.join(ForkJoinTask.java:651) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.joinConcurrentTasksInReverseOrderToEnableWorkStealing(ForkJoinPoolHierarchicalTestExecutorService.java:179) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.invokeAll(ForkJoinPoolHierarchicalTestExecutorService.java:153) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) - at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) - at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) - at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312) - at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843) - at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808) - at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188) - -java.lang.NullPointerException: Cannot invoke "javax.lang.model.type.TypeMirror.toString()" because "t" is null - at jdk.compiler/com.sun.tools.javac.model.JavacTypes.getDeclaredType0(JavacTypes.java:272) - at jdk.compiler/com.sun.tools.javac.model.JavacTypes.getDeclaredType(JavacTypes.java:241) - at jdk.compiler/com.sun.tools.javac.model.JavacTypes.getDeclaredType(JavacTypes.java:249) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang.declaredType(Lang.java:1387) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang$ConstableTypeAndElementSource.declaredType(Lang.java:2016) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.bean(DefaultBeanSet.java:223) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:177) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:137) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:109) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.TestDefaultBeanSet.setup(TestDefaultBeanSet.java:74) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) - at java.base/java.lang.reflect.Method.invoke(Method.java:578) - at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727) - at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) - at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptLifecycleMethod(TimeoutExtension.java:128) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptBeforeEachMethod(TimeoutExtension.java:78) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeMethodInExtensionContext(ClassBasedTestDescriptor.java:520) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$synthesizeBeforeEachMethodAdapter$23(ClassBasedTestDescriptor.java:505) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeEachMethods$3(TestMethodTestDescriptor.java:174) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeMethodsOrCallbacksUntilExceptionOccurs$6(TestMethodTestDescriptor.java:202) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeMethodsOrCallbacksUntilExceptionOccurs(TestMethodTestDescriptor.java:202) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeEachMethods(TestMethodTestDescriptor.java:171) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:134) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) - at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) - at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) - at java.base/java.util.concurrent.ForkJoinPool.helpJoin(ForkJoinPool.java:2076) - at java.base/java.util.concurrent.ForkJoinTask.awaitDone(ForkJoinTask.java:423) - at java.base/java.util.concurrent.ForkJoinTask.join(ForkJoinTask.java:651) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.joinConcurrentTasksInReverseOrderToEnableWorkStealing(ForkJoinPoolHierarchicalTestExecutorService.java:179) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.invokeAll(ForkJoinPoolHierarchicalTestExecutorService.java:153) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) - at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) - at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) - at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312) - at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843) - at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808) - at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188) - -java.lang.Exception: Stack trace - at java.base/java.lang.Thread.dumpStack(Thread.java:2246) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang.declaredType(Lang.java:1373) - at java.base/java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:733) - at java.base/java.lang.invoke.MethodHandleImpl$AsVarargsCollector.invokeWithArguments(MethodHandleImpl.java:538) - at java.base/java.lang.invoke.ConstantBootstraps.invoke(ConstantBootstraps.java:262) - at java.base/java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:733) - at java.base/java.lang.invoke.MethodHandleImpl$AsVarargsCollector.invokeWithArguments(MethodHandleImpl.java:538) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:268) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.TestConstableSemantics.testId(TestConstableSemantics.java:74) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) - at java.base/java.lang.reflect.Method.invoke(Method.java:578) - at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727) - at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) - at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:147) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:86) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:217) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:213) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:138) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) - at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) - at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) - at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312) - at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843) - at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808) - at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188) - -java.lang.BootstrapMethodError: java.lang.NullPointerException - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:272) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.TestConstableSemantics.testId(TestConstableSemantics.java:74) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) - at java.base/java.lang.reflect.Method.invoke(Method.java:578) - at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727) - at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) - at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:147) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:86) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:217) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:213) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:138) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) - at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) - at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) - at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312) - at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843) - at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808) - at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188) -Caused by: java.lang.NullPointerException - at java.base/java.util.Objects.requireNonNull(Objects.java:233) - at java.base/java.util.ImmutableCollections$List12.(ImmutableCollections.java:563) - at java.base/java.util.List.of(List.java:829) - at java.base/java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:733) - at java.base/java.lang.invoke.ConstantBootstraps.invoke(ConstantBootstraps.java:262) - at java.base/java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:733) - at java.base/java.lang.invoke.MethodHandleImpl$AsVarargsCollector.invokeWithArguments(MethodHandleImpl.java:538) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:268) - ... 39 more - -java.lang.AssertionError - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang.declaredType(Lang.java:1378) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang$ConstableTypeAndElementSource.declaredType(Lang.java:2026) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.TypeAndElementSource.declaredType(TypeAndElementSource.java:56) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.bean(DefaultBeanSet.java:221) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:177) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:137) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:109) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.TestDefaultBeanSet.setup(TestDefaultBeanSet.java:74) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) - at java.base/java.lang.reflect.Method.invoke(Method.java:578) - at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727) - at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) - at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptLifecycleMethod(TimeoutExtension.java:128) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptBeforeEachMethod(TimeoutExtension.java:78) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeMethodInExtensionContext(ClassBasedTestDescriptor.java:520) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$synthesizeBeforeEachMethodAdapter$23(ClassBasedTestDescriptor.java:505) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeEachMethods$3(TestMethodTestDescriptor.java:174) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeMethodsOrCallbacksUntilExceptionOccurs$6(TestMethodTestDescriptor.java:202) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeMethodsOrCallbacksUntilExceptionOccurs(TestMethodTestDescriptor.java:202) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeEachMethods(TestMethodTestDescriptor.java:171) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:134) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) - at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) - at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) - at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312) - at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843) - at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808) - at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188) - -[ERROR] org.microbean.bean.TestDefaultBeanSet.testConstableStuff Time elapsed: 0.023 s <<< FAILURE! -java.lang.AssertionError - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang.declaredType(Lang.java:1378) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang$ConstableTypeAndElementSource.declaredType(Lang.java:2026) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.TypeAndElementSource.declaredType(TypeAndElementSource.java:56) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.bean(DefaultBeanSet.java:221) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:177) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:137) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:109) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.TestDefaultBeanSet.setup(TestDefaultBeanSet.java:74) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) - at java.base/java.lang.reflect.Method.invoke(Method.java:578) - at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727) - at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) - at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptLifecycleMethod(TimeoutExtension.java:128) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptBeforeEachMethod(TimeoutExtension.java:78) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeMethodInExtensionContext(ClassBasedTestDescriptor.java:520) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$synthesizeBeforeEachMethodAdapter$23(ClassBasedTestDescriptor.java:505) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeEachMethods$3(TestMethodTestDescriptor.java:174) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeMethodsOrCallbacksUntilExceptionOccurs$6(TestMethodTestDescriptor.java:202) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeMethodsOrCallbacksUntilExceptionOccurs(TestMethodTestDescriptor.java:202) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeEachMethods(TestMethodTestDescriptor.java:171) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:134) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) - at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) - at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) - at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.tryRemoveAndExec(ForkJoinPool.java:1351) - at java.base/java.util.concurrent.ForkJoinTask.awaitDone(ForkJoinTask.java:422) - at java.base/java.util.concurrent.ForkJoinTask.join(ForkJoinTask.java:651) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.joinConcurrentTasksInReverseOrderToEnableWorkStealing(ForkJoinPoolHierarchicalTestExecutorService.java:179) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.invokeAll(ForkJoinPoolHierarchicalTestExecutorService.java:153) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) - at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) - at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) - at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312) - at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843) - at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808) - at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188) - -java.lang.NullPointerException: Cannot invoke "javax.lang.model.element.Parameterizable.getTypeParameters()" because the return value of "org.microbean.lang.Lang.parameterizable(java.lang.reflect.GenericDeclaration)" is null - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang.typeParameterElement(Lang.java:1647) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang.typeVariable(Lang.java:1661) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.TestReferenceTypeList.testTypeVariables(TestReferenceTypeList.java:114) - -java.lang.AssertionError: null type element for module org.microbean.bean and org.microbean.bean.Alternate.Resolver - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.TypeAndElementSource.typeElement(TypeAndElementSource.java:140) - - -2023-08-30 4:11 PM PDT - -This stack is getting closer to resolution. This is triggered by a (parallel) run of unit tests, and only extremely -infrequently. - -When it happens, it is because some way, somehow, Lang#describeConstable(TypeElement) is causing a DynamicConstantDesc -of NULL to be created for the module housing java.lang.String or java.lang.Object (!). (Obviously this should be a -DynamicConstantDesc representing java.base.) - -This *could* end up being a bug in the javac compiler. Or it can be a problem with the root set of modules. See -https://github.com/openjdk/jdk/blob/jdk-22%2B13/src/jdk.compiler/share/classes/com/sun/tools/javac/model/JavacElements.java#L453-L461. - -[ERROR] org.microbean.bean2.TestConstableSemantics.testReferenceTypeList Time elapsed: 0.227 s <<< ERROR! -java.lang.BootstrapMethodError: java.lang.NullPointerException: moduleElement - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:272) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.TestConstableSemantics.testReferenceTypeList(TestConstableSemantics.java:65) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) - at java.base/java.lang.reflect.Method.invoke(Method.java:578) - at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727) - at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) - at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestableMethod(TimeoutExtension.java:147) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptTestMethod(TimeoutExtension.java:86) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeTestMethod$7(TestMethodTestDescriptor.java:217) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeTestMethod(TestMethodTestDescriptor.java:213) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:138) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) - at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) - at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) - at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.tryRemoveAndExec(ForkJoinPool.java:1351) - at java.base/java.util.concurrent.ForkJoinTask.awaitDone(ForkJoinTask.java:422) - at java.base/java.util.concurrent.ForkJoinTask.join(ForkJoinTask.java:651) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.joinConcurrentTasksInReverseOrderToEnableWorkStealing(ForkJoinPoolHierarchicalTestExecutorService.java:179) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.invokeAll(ForkJoinPoolHierarchicalTestExecutorService.java:153) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) - at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) - at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) - at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312) - at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843) - at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808) - at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188) -Caused by: java.lang.NullPointerException: moduleElement - at java.base/java.util.Objects.requireNonNull(Objects.java:259) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang.typeElement(Lang.java:1662) - at java.base/java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:733) - at java.base/java.lang.invoke.ConstantBootstraps.invoke(ConstantBootstraps.java:262) - at java.base/java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:733) - at java.base/java.lang.invoke.MethodHandleImpl$AsVarargsCollector.invokeWithArguments(MethodHandleImpl.java:538) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:268) - -2023-08-31 - -[ERROR] org.microbean.bean2.TestReferenceTypeList.testTypeVariables Time elapsed: 0.037 s <<< ERROR! -java.lang.IllegalArgumentException: t: java.lang.invoke.TypeDescriptor.OfField; t.getKind(): ERROR - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.visitor.TypeClosureVisitor.defaultAction(TypeClosureVisitor.java:56) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.visitor.TypeClosureVisitor.defaultAction(TypeClosureVisitor.java:30) - at java.compiler@20/javax.lang.model.util.SimpleTypeVisitor6.visitError(SimpleTypeVisitor6.java:190) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.type.DelegatingTypeMirror.accept(DelegatingTypeMirror.java:101) - at java.compiler@20/javax.lang.model.util.AbstractTypeVisitor6.visit(AbstractTypeVisitor6.java:104) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.visitor.TypeClosureVisitor.visitDeclaredOrIntersectionOrTypeVariable(TypeClosureVisitor.java:142) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.visitor.TypeClosureVisitor.visitDeclared(TypeClosureVisitor.java:62) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.visitor.TypeClosureVisitor.visitDeclared(TypeClosureVisitor.java:30) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.type.DelegatingTypeMirror.accept(DelegatingTypeMirror.java:100) - at java.compiler@20/javax.lang.model.util.AbstractTypeVisitor6.visit(AbstractTypeVisitor6.java:104) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.visitor.TypeClosureVisitor.visitDeclaredOrIntersectionOrTypeVariable(TypeClosureVisitor.java:142) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.visitor.TypeClosureVisitor.visitTypeVariable(TypeClosureVisitor.java:74) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.visitor.TypeClosureVisitor.visitTypeVariable(TypeClosureVisitor.java:30) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.type.DelegatingTypeMirror.accept(DelegatingTypeMirror.java:107) - at java.compiler@20/javax.lang.model.util.AbstractTypeVisitor6.visit(AbstractTypeVisitor6.java:104) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.ReferenceTypeList.closure(ReferenceTypeList.java:299) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.ReferenceTypeList.closure(ReferenceTypeList.java:287) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.TestReferenceTypeList.testTypeVariables(TestReferenceTypeList.java:119) - -[ERROR] org.microbean.bean2.TestConstableSemantics.testId Time elapsed: 0.029 s <<< ERROR! -java.lang.BootstrapMethodError: java.lang.NullPointerException: moduleElement; canonicalName: java.lang.Object - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:272) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.TestConstableSemantics.testId(TestConstableSemantics.java:75) - -[ERROR] org.microbean.bean2.TestConstableSemantics.testReferenceTypeList Time elapsed: 0.236 s <<< ERROR! -java.lang.BootstrapMethodError: java.lang.NullPointerException: e - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:272) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.TestConstableSemantics.testReferenceTypeList(TestConstableSemantics.java:66) - -[ERROR] org.microbean.bean2.TestDefaultBeanSet.testTypeElementResolution Time elapsed: 0.003 s <<< ERROR! -java.lang.NullPointerException: moduleElement; canonicalName: org.microbean.bean2.DefaultBeanSet - at java.base/java.util.Objects.requireNonNull(Objects.java:259) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang.typeElement(Lang.java:1679) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang$ConstableTypeAndElementSource.typeElement(Lang.java:2153) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.TypeAndElementSource.typeElement(TypeAndElementSource.java:130) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.TypeAndElementSource.declaredType(TypeAndElementSource.java:61) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.DefaultBeanSet.bean(DefaultBeanSet.java:212) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.DefaultBeanSet.(DefaultBeanSet.java:177) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.DefaultBeanSet.(DefaultBeanSet.java:137) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.DefaultBeanSet.(DefaultBeanSet.java:109) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.TestDefaultBeanSet.setup(TestDefaultBeanSet.java:80) - -[ERROR] org.microbean.bean2.TestDefaultBeanSet.testConstableStuff Time elapsed: 0.02 s <<< ERROR! -java.lang.BootstrapMethodError: java.lang.NullPointerException: moduleElement; canonicalName: java.lang.Object - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:272) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.TestDefaultBeanSet$1ConstantDefaultBeanSet.(TestDefaultBeanSet.java:112) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.TestDefaultBeanSet.testConstableStuff(TestDefaultBeanSet.java:115) - -[ERROR] org.microbean.bean2.TestConstableSemantics.testReferenceTypeList Time elapsed: 0.227 s <<< ERROR! -java.lang.BootstrapMethodError: java.lang.NullPointerException: moduleElement; canonicalName: java.lang.String - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:272) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.TestConstableSemantics.testReferenceTypeList(TestConstableSemantics.java:66) - -[ERROR] org.microbean.bean2.TestDefaultBeanSet.testConstableStuff Time elapsed: 0.026 s <<< ERROR! -java.lang.NullPointerException: moduleElement; canonicalName: org.microbean.bean2.DefaultBeanSet - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang.typeElement(Lang.java:1680) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang$ConstableTypeAndElementSource.typeElement(Lang.java:2025) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.TypeAndElementSource.typeElement(TypeAndElementSource.java:130) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.TypeAndElementSource.declaredType(TypeAndElementSource.java:61) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.DefaultBeanSet.bean(DefaultBeanSet.java:212) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.DefaultBeanSet.(DefaultBeanSet.java:177) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.DefaultBeanSet.(DefaultBeanSet.java:137) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.DefaultBeanSet.(DefaultBeanSet.java:109) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.TestDefaultBeanSet$1ConstantDefaultBeanSet.(TestDefaultBeanSet.java:112) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.TestDefaultBeanSet.testConstableStuff(TestDefaultBeanSet.java:115) - -[ERROR] org.microbean.bean2.TestReferenceTypeList.testTypeVariables Time elapsed: 0.002 s <<< FAILURE! -java.lang.AssertionError: null moduleElement for org.microbean.bean - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang.moduleElement(Lang.java:1266) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang.typeElement(Lang.java:1651) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang.executableElement(Lang.java:1491) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang.executableElement(Lang.java:1479) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang.parameterizable(Lang.java:1733) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang.typeParameterElement(Lang.java:1750) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang.typeVariable(Lang.java:1764) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.TestReferenceTypeList.testTypeVariables(TestReferenceTypeList.java:114) - -This one's wild. If you trace it, you get to JavacElements#doGetElement(ModuleElement, String, CharSequence, -Class). None of the arguments is null: - -[ERROR] org.microbean.bean2.TestDefaultBeanSet.testBeans Time elapsed: 0.03 s <<< FAILURE! -java.lang.AssertionError: null type element for module org.microbean.bean and org.microbean.bean2.DefaultBeanSet - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.TypeAndElementSource.typeElement(TypeAndElementSource.java:131) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.TypeAndElementSource.declaredType(TypeAndElementSource.java:61) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.DefaultBeanSet.bean(DefaultBeanSet.java:212) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.DefaultBeanSet.(DefaultBeanSet.java:177) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.DefaultBeanSet.(DefaultBeanSet.java:137) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.DefaultBeanSet.(DefaultBeanSet.java:109) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.TestDefaultBeanSet.setup(TestDefaultBeanSet.java:80) - - -This is also very strange; note "k8va". I wonder if the name table has a bug in it. - -Aug 31, 2023 5:44:27 PM org.microbean.lang.Lang moduleElement -FINE: null ModuleElement for module name k8va.base - -[ERROR] Tests run: 6, Failures: 0, Errors: 1, Skipped: 0, Time elapsed: 0.273 s <<< FAILURE! - in org.microbean.bean2.TestReferenceTypeList -[ERROR] org.microbean.bean2.TestConstableSemantics.testId Time elapsed: 0.252 s <<< ERROR! -java.lang.BootstrapMethodError: java.lang.NullPointerException: moduleElement; canonicalName: java.lang.Object - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:272) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.TestConstableSemantics.testId(TestConstableSemantics.java:75) - -Aug 31, 2023 8:06:51 PM org.microbean.lang.Lang typeElement -FINE: null TypeElement for ModuleElement org.microbean.bean and canonicalName org.microbean.bean2.DefaultBeanSet -[ERROR] Tests run: 5, Failures: 3, Errors: 0, Skipped: 0, Time elapsed: 0.267 s <<< FAILURE! - in org.microbean.bean2.TestDefaultBeanSet -[ERROR] org.microbean.bean2.TestDefaultBeanSet.testTypeElementResolution Time elapsed: 0.009 s <<< FAILURE! -java.lang.AssertionError: null type element for module org.microbean.bean and org.microbean.bean2.DefaultBeanSet - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.TypeAndElementSource.typeElement(TypeAndElementSource.java:131) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.TypeAndElementSource.declaredType(TypeAndElementSource.java:61) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.DefaultBeanSet.bean(DefaultBeanSet.java:212) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.DefaultBeanSet.(DefaultBeanSet.java:177) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.DefaultBeanSet.(DefaultBeanSet.java:137) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.DefaultBeanSet.(DefaultBeanSet.java:109) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.TestDefaultBeanSet.setup(TestDefaultBeanSet.java:80) - -More wildness: - -FINE: null TypeElement for ModuleElement java.base and canonicalName jupiterorg.junit - -I'm beginning to think that Name.toString() when backed by the compiler is not thread-safe. - -[ERROR] org.microbean.bean2.TestConstableSemantics.testId Time elapsed: 0.211 s <<< ERROR! -java.lang.BootstrapMethodError: java.lang.NullPointerException: e - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:272) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.TestConstableSemantics.testId(TestConstableSemantics.java:75) - -With useUnsharedTable everything seems to work fine. - -When I restore the default behavior (shared name table): - -[ERROR] org.microbean.bean2.TestReferenceTypeList.testSorting Time elapsed: 0.003 s <<< ERROR! -java.lang.NullPointerException: e - at java.base/java.util.Objects.requireNonNull(Objects.java:259) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang.unwrap(Lang.java:2155) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang.declaredType(Lang.java:1624) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.TestReferenceTypeList.testSorting(TestReferenceTypeList.java:66) - -[INFO] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.31 s - in org.microbean.bean2.TestConstableSemantics -Sep 06, 2023 8:02:06 PM org.microbean.lang.Lang moduleElement -FINE: null ModuleElement for module name MethodOrd <-- NOTE -Sep 06, 2023 8:02:06 PM org.microbean.lang.Lang moduleElement -FINE: class org.microbean.lang.Lang (in module org.microbean.lang) cannot access a member of class com.sun.tools.javac.model.JavacElements (in module jdk.compiler) with modifiers "private final" -java.lang.IllegalAccessException: class org.microbean.lang.Lang (in module org.microbean.lang) cannot access a member of class com.sun.tools.javac.model.JavacElements (in module jdk.compiler) with modifiers "private final" - at java.base/jdk.internal.reflect.Reflection.newIllegalAccessException(Reflection.java:394) - at java.base/java.lang.reflect.AccessibleObject.checkAccess(AccessibleObject.java:709) - at java.base/java.lang.reflect.Field.checkAccess(Field.java:1154) - at java.base/java.lang.reflect.Field.get(Field.java:439) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang.moduleElement(Lang.java:1424) - at java.base/java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:733) - at java.base/java.lang.invoke.ConstantBootstraps.invoke(ConstantBootstraps.java:262) - at java.base/java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:733) - at java.base/java.lang.invoke.MethodHandleImpl$AsVarargsCollector.invokeWithArguments(MethodHandleImpl.java:538) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:268) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at java.base/java.lang.constant.DynamicConstantDesc.resolveConstantDesc(DynamicConstantDesc.java:266) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.TestDefaultBeanSet$1ConstantDefaultBeanSet.(TestDefaultBeanSet.java:112) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean2.TestDefaultBeanSet.testConstableStuff(TestDefaultBeanSet.java:115) - - diff --git a/mvnw b/mvnw new file mode 100755 index 0000000..19529dd --- /dev/null +++ b/mvnw @@ -0,0 +1,259 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you 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. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Apache Maven Wrapper startup batch script, version 3.3.2 +# +# Optional ENV vars +# ----------------- +# JAVA_HOME - location of a JDK home dir, required when download maven via java source +# MVNW_REPOURL - repo url base for downloading maven distribution +# MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +# MVNW_VERBOSE - true: enable verbose log; debug: trace the mvnw script; others: silence the output +# ---------------------------------------------------------------------------- + +set -euf +[ "${MVNW_VERBOSE-}" != debug ] || set -x + +# OS specific support. +native_path() { printf %s\\n "$1"; } +case "$(uname)" in +CYGWIN* | MINGW*) + [ -z "${JAVA_HOME-}" ] || JAVA_HOME="$(cygpath --unix "$JAVA_HOME")" + native_path() { cygpath --path --windows "$1"; } + ;; +esac + +# set JAVACMD and JAVACCMD +set_java_home() { + # For Cygwin and MinGW, ensure paths are in Unix format before anything is touched + if [ -n "${JAVA_HOME-}" ]; then + if [ -x "$JAVA_HOME/jre/sh/java" ]; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + JAVACCMD="$JAVA_HOME/jre/sh/javac" + else + JAVACMD="$JAVA_HOME/bin/java" + JAVACCMD="$JAVA_HOME/bin/javac" + + if [ ! -x "$JAVACMD" ] || [ ! -x "$JAVACCMD" ]; then + echo "The JAVA_HOME environment variable is not defined correctly, so mvnw cannot run." >&2 + echo "JAVA_HOME is set to \"$JAVA_HOME\", but \"\$JAVA_HOME/bin/java\" or \"\$JAVA_HOME/bin/javac\" does not exist." >&2 + return 1 + fi + fi + else + JAVACMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v java + )" || : + JAVACCMD="$( + 'set' +e + 'unset' -f command 2>/dev/null + 'command' -v javac + )" || : + + if [ ! -x "${JAVACMD-}" ] || [ ! -x "${JAVACCMD-}" ]; then + echo "The java/javac command does not exist in PATH nor is JAVA_HOME set, so mvnw cannot run." >&2 + return 1 + fi + fi +} + +# hash string like Java String::hashCode +hash_string() { + str="${1:-}" h=0 + while [ -n "$str" ]; do + char="${str%"${str#?}"}" + h=$(((h * 31 + $(LC_CTYPE=C printf %d "'$char")) % 4294967296)) + str="${str#?}" + done + printf %x\\n $h +} + +verbose() { :; } +[ "${MVNW_VERBOSE-}" != true ] || verbose() { printf %s\\n "${1-}"; } + +die() { + printf %s\\n "$1" >&2 + exit 1 +} + +trim() { + # MWRAPPER-139: + # Trims trailing and leading whitespace, carriage returns, tabs, and linefeeds. + # Needed for removing poorly interpreted newline sequences when running in more + # exotic environments such as mingw bash on Windows. + printf "%s" "${1}" | tr -d '[:space:]' +} + +# parse distributionUrl and optional distributionSha256Sum, requires .mvn/wrapper/maven-wrapper.properties +while IFS="=" read -r key value; do + case "${key-}" in + distributionUrl) distributionUrl=$(trim "${value-}") ;; + distributionSha256Sum) distributionSha256Sum=$(trim "${value-}") ;; + esac +done <"${0%/*}/.mvn/wrapper/maven-wrapper.properties" +[ -n "${distributionUrl-}" ] || die "cannot read distributionUrl property in ${0%/*}/.mvn/wrapper/maven-wrapper.properties" + +case "${distributionUrl##*/}" in +maven-mvnd-*bin.*) + MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ + case "${PROCESSOR_ARCHITECTURE-}${PROCESSOR_ARCHITEW6432-}:$(uname -a)" in + *AMD64:CYGWIN* | *AMD64:MINGW*) distributionPlatform=windows-amd64 ;; + :Darwin*x86_64) distributionPlatform=darwin-amd64 ;; + :Darwin*arm64) distributionPlatform=darwin-aarch64 ;; + :Linux*x86_64*) distributionPlatform=linux-amd64 ;; + *) + echo "Cannot detect native platform for mvnd on $(uname)-$(uname -m), use pure java version" >&2 + distributionPlatform=linux-amd64 + ;; + esac + distributionUrl="${distributionUrl%-bin.*}-$distributionPlatform.zip" + ;; +maven-mvnd-*) MVN_CMD=mvnd.sh _MVNW_REPO_PATTERN=/maven/mvnd/ ;; +*) MVN_CMD="mvn${0##*/mvnw}" _MVNW_REPO_PATTERN=/org/apache/maven/ ;; +esac + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +[ -z "${MVNW_REPOURL-}" ] || distributionUrl="$MVNW_REPOURL$_MVNW_REPO_PATTERN${distributionUrl#*"$_MVNW_REPO_PATTERN"}" +distributionUrlName="${distributionUrl##*/}" +distributionUrlNameMain="${distributionUrlName%.*}" +distributionUrlNameMain="${distributionUrlNameMain%-bin}" +MAVEN_USER_HOME="${MAVEN_USER_HOME:-${HOME}/.m2}" +MAVEN_HOME="${MAVEN_USER_HOME}/wrapper/dists/${distributionUrlNameMain-}/$(hash_string "$distributionUrl")" + +exec_maven() { + unset MVNW_VERBOSE MVNW_USERNAME MVNW_PASSWORD MVNW_REPOURL || : + exec "$MAVEN_HOME/bin/$MVN_CMD" "$@" || die "cannot exec $MAVEN_HOME/bin/$MVN_CMD" +} + +if [ -d "$MAVEN_HOME" ]; then + verbose "found existing MAVEN_HOME at $MAVEN_HOME" + exec_maven "$@" +fi + +case "${distributionUrl-}" in +*?-bin.zip | *?maven-mvnd-?*-?*.zip) ;; +*) die "distributionUrl is not valid, must match *-bin.zip or maven-mvnd-*.zip, but found '${distributionUrl-}'" ;; +esac + +# prepare tmp dir +if TMP_DOWNLOAD_DIR="$(mktemp -d)" && [ -d "$TMP_DOWNLOAD_DIR" ]; then + clean() { rm -rf -- "$TMP_DOWNLOAD_DIR"; } + trap clean HUP INT TERM EXIT +else + die "cannot create temp dir" +fi + +mkdir -p -- "${MAVEN_HOME%/*}" + +# Download and Install Apache Maven +verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +verbose "Downloading from: $distributionUrl" +verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +# select .zip or .tar.gz +if ! command -v unzip >/dev/null; then + distributionUrl="${distributionUrl%.zip}.tar.gz" + distributionUrlName="${distributionUrl##*/}" +fi + +# verbose opt +__MVNW_QUIET_WGET=--quiet __MVNW_QUIET_CURL=--silent __MVNW_QUIET_UNZIP=-q __MVNW_QUIET_TAR='' +[ "${MVNW_VERBOSE-}" != true ] || __MVNW_QUIET_WGET='' __MVNW_QUIET_CURL='' __MVNW_QUIET_UNZIP='' __MVNW_QUIET_TAR=v + +# normalize http auth +case "${MVNW_PASSWORD:+has-password}" in +'') MVNW_USERNAME='' MVNW_PASSWORD='' ;; +has-password) [ -n "${MVNW_USERNAME-}" ] || MVNW_USERNAME='' MVNW_PASSWORD='' ;; +esac + +if [ -z "${MVNW_USERNAME-}" ] && command -v wget >/dev/null; then + verbose "Found wget ... using wget" + wget ${__MVNW_QUIET_WGET:+"$__MVNW_QUIET_WGET"} "$distributionUrl" -O "$TMP_DOWNLOAD_DIR/$distributionUrlName" || die "wget: Failed to fetch $distributionUrl" +elif [ -z "${MVNW_USERNAME-}" ] && command -v curl >/dev/null; then + verbose "Found curl ... using curl" + curl ${__MVNW_QUIET_CURL:+"$__MVNW_QUIET_CURL"} -f -L -o "$TMP_DOWNLOAD_DIR/$distributionUrlName" "$distributionUrl" || die "curl: Failed to fetch $distributionUrl" +elif set_java_home; then + verbose "Falling back to use Java to download" + javaSource="$TMP_DOWNLOAD_DIR/Downloader.java" + targetZip="$TMP_DOWNLOAD_DIR/$distributionUrlName" + cat >"$javaSource" <<-END + public class Downloader extends java.net.Authenticator + { + protected java.net.PasswordAuthentication getPasswordAuthentication() + { + return new java.net.PasswordAuthentication( System.getenv( "MVNW_USERNAME" ), System.getenv( "MVNW_PASSWORD" ).toCharArray() ); + } + public static void main( String[] args ) throws Exception + { + setDefault( new Downloader() ); + java.nio.file.Files.copy( java.net.URI.create( args[0] ).toURL().openStream(), java.nio.file.Paths.get( args[1] ).toAbsolutePath().normalize() ); + } + } + END + # For Cygwin/MinGW, switch paths to Windows format before running javac and java + verbose " - Compiling Downloader.java ..." + "$(native_path "$JAVACCMD")" "$(native_path "$javaSource")" || die "Failed to compile Downloader.java" + verbose " - Running Downloader.java ..." + "$(native_path "$JAVACMD")" -cp "$(native_path "$TMP_DOWNLOAD_DIR")" Downloader "$distributionUrl" "$(native_path "$targetZip")" +fi + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +if [ -n "${distributionSha256Sum-}" ]; then + distributionSha256Result=false + if [ "$MVN_CMD" = mvnd.sh ]; then + echo "Checksum validation is not supported for maven-mvnd." >&2 + echo "Please disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + elif command -v sha256sum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | sha256sum -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + elif command -v shasum >/dev/null; then + if echo "$distributionSha256Sum $TMP_DOWNLOAD_DIR/$distributionUrlName" | shasum -a 256 -c >/dev/null 2>&1; then + distributionSha256Result=true + fi + else + echo "Checksum validation was requested but neither 'sha256sum' or 'shasum' are available." >&2 + echo "Please install either command, or disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." >&2 + exit 1 + fi + if [ $distributionSha256Result = false ]; then + echo "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised." >&2 + echo "If you updated your Maven version, you need to update the specified distributionSha256Sum property." >&2 + exit 1 + fi +fi + +# unzip and move +if command -v unzip >/dev/null; then + unzip ${__MVNW_QUIET_UNZIP:+"$__MVNW_QUIET_UNZIP"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -d "$TMP_DOWNLOAD_DIR" || die "failed to unzip" +else + tar xzf${__MVNW_QUIET_TAR:+"$__MVNW_QUIET_TAR"} "$TMP_DOWNLOAD_DIR/$distributionUrlName" -C "$TMP_DOWNLOAD_DIR" || die "failed to untar" +fi +printf %s\\n "$distributionUrl" >"$TMP_DOWNLOAD_DIR/$distributionUrlNameMain/mvnw.url" +mv -- "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" "$MAVEN_HOME" || [ -d "$MAVEN_HOME" ] || die "fail to move MAVEN_HOME" + +clean || : +exec_maven "$@" diff --git a/mvnw.cmd b/mvnw.cmd new file mode 100644 index 0000000..b150b91 --- /dev/null +++ b/mvnw.cmd @@ -0,0 +1,149 @@ +<# : batch portion +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Apache Maven Wrapper startup batch script, version 3.3.2 +@REM +@REM Optional ENV vars +@REM MVNW_REPOURL - repo url base for downloading maven distribution +@REM MVNW_USERNAME/MVNW_PASSWORD - user and password for downloading maven +@REM MVNW_VERBOSE - true: enable verbose log; others: silence the output +@REM ---------------------------------------------------------------------------- + +@IF "%__MVNW_ARG0_NAME__%"=="" (SET __MVNW_ARG0_NAME__=%~nx0) +@SET __MVNW_CMD__= +@SET __MVNW_ERROR__= +@SET __MVNW_PSMODULEP_SAVE=%PSModulePath% +@SET PSModulePath= +@FOR /F "usebackq tokens=1* delims==" %%A IN (`powershell -noprofile "& {$scriptDir='%~dp0'; $script='%__MVNW_ARG0_NAME__%'; icm -ScriptBlock ([Scriptblock]::Create((Get-Content -Raw '%~f0'))) -NoNewScope}"`) DO @( + IF "%%A"=="MVN_CMD" (set __MVNW_CMD__=%%B) ELSE IF "%%B"=="" (echo %%A) ELSE (echo %%A=%%B) +) +@SET PSModulePath=%__MVNW_PSMODULEP_SAVE% +@SET __MVNW_PSMODULEP_SAVE= +@SET __MVNW_ARG0_NAME__= +@SET MVNW_USERNAME= +@SET MVNW_PASSWORD= +@IF NOT "%__MVNW_CMD__%"=="" (%__MVNW_CMD__% %*) +@echo Cannot start maven from wrapper >&2 && exit /b 1 +@GOTO :EOF +: end batch / begin powershell #> + +$ErrorActionPreference = "Stop" +if ($env:MVNW_VERBOSE -eq "true") { + $VerbosePreference = "Continue" +} + +# calculate distributionUrl, requires .mvn/wrapper/maven-wrapper.properties +$distributionUrl = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionUrl +if (!$distributionUrl) { + Write-Error "cannot read distributionUrl property in $scriptDir/.mvn/wrapper/maven-wrapper.properties" +} + +switch -wildcard -casesensitive ( $($distributionUrl -replace '^.*/','') ) { + "maven-mvnd-*" { + $USE_MVND = $true + $distributionUrl = $distributionUrl -replace '-bin\.[^.]*$',"-windows-amd64.zip" + $MVN_CMD = "mvnd.cmd" + break + } + default { + $USE_MVND = $false + $MVN_CMD = $script -replace '^mvnw','mvn' + break + } +} + +# apply MVNW_REPOURL and calculate MAVEN_HOME +# maven home pattern: ~/.m2/wrapper/dists/{apache-maven-,maven-mvnd--}/ +if ($env:MVNW_REPOURL) { + $MVNW_REPO_PATTERN = if ($USE_MVND) { "/org/apache/maven/" } else { "/maven/mvnd/" } + $distributionUrl = "$env:MVNW_REPOURL$MVNW_REPO_PATTERN$($distributionUrl -replace '^.*'+$MVNW_REPO_PATTERN,'')" +} +$distributionUrlName = $distributionUrl -replace '^.*/','' +$distributionUrlNameMain = $distributionUrlName -replace '\.[^.]*$','' -replace '-bin$','' +$MAVEN_HOME_PARENT = "$HOME/.m2/wrapper/dists/$distributionUrlNameMain" +if ($env:MAVEN_USER_HOME) { + $MAVEN_HOME_PARENT = "$env:MAVEN_USER_HOME/wrapper/dists/$distributionUrlNameMain" +} +$MAVEN_HOME_NAME = ([System.Security.Cryptography.MD5]::Create().ComputeHash([byte[]][char[]]$distributionUrl) | ForEach-Object {$_.ToString("x2")}) -join '' +$MAVEN_HOME = "$MAVEN_HOME_PARENT/$MAVEN_HOME_NAME" + +if (Test-Path -Path "$MAVEN_HOME" -PathType Container) { + Write-Verbose "found existing MAVEN_HOME at $MAVEN_HOME" + Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" + exit $? +} + +if (! $distributionUrlNameMain -or ($distributionUrlName -eq $distributionUrlNameMain)) { + Write-Error "distributionUrl is not valid, must end with *-bin.zip, but found $distributionUrl" +} + +# prepare tmp dir +$TMP_DOWNLOAD_DIR_HOLDER = New-TemporaryFile +$TMP_DOWNLOAD_DIR = New-Item -Itemtype Directory -Path "$TMP_DOWNLOAD_DIR_HOLDER.dir" +$TMP_DOWNLOAD_DIR_HOLDER.Delete() | Out-Null +trap { + if ($TMP_DOWNLOAD_DIR.Exists) { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } + } +} + +New-Item -Itemtype Directory -Path "$MAVEN_HOME_PARENT" -Force | Out-Null + +# Download and Install Apache Maven +Write-Verbose "Couldn't find MAVEN_HOME, downloading and installing it ..." +Write-Verbose "Downloading from: $distributionUrl" +Write-Verbose "Downloading to: $TMP_DOWNLOAD_DIR/$distributionUrlName" + +$webclient = New-Object System.Net.WebClient +if ($env:MVNW_USERNAME -and $env:MVNW_PASSWORD) { + $webclient.Credentials = New-Object System.Net.NetworkCredential($env:MVNW_USERNAME, $env:MVNW_PASSWORD) +} +[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12 +$webclient.DownloadFile($distributionUrl, "$TMP_DOWNLOAD_DIR/$distributionUrlName") | Out-Null + +# If specified, validate the SHA-256 sum of the Maven distribution zip file +$distributionSha256Sum = (Get-Content -Raw "$scriptDir/.mvn/wrapper/maven-wrapper.properties" | ConvertFrom-StringData).distributionSha256Sum +if ($distributionSha256Sum) { + if ($USE_MVND) { + Write-Error "Checksum validation is not supported for maven-mvnd. `nPlease disable validation by removing 'distributionSha256Sum' from your maven-wrapper.properties." + } + Import-Module $PSHOME\Modules\Microsoft.PowerShell.Utility -Function Get-FileHash + if ((Get-FileHash "$TMP_DOWNLOAD_DIR/$distributionUrlName" -Algorithm SHA256).Hash.ToLower() -ne $distributionSha256Sum) { + Write-Error "Error: Failed to validate Maven distribution SHA-256, your Maven distribution might be compromised. If you updated your Maven version, you need to update the specified distributionSha256Sum property." + } +} + +# unzip and move +Expand-Archive "$TMP_DOWNLOAD_DIR/$distributionUrlName" -DestinationPath "$TMP_DOWNLOAD_DIR" | Out-Null +Rename-Item -Path "$TMP_DOWNLOAD_DIR/$distributionUrlNameMain" -NewName $MAVEN_HOME_NAME | Out-Null +try { + Move-Item -Path "$TMP_DOWNLOAD_DIR/$MAVEN_HOME_NAME" -Destination $MAVEN_HOME_PARENT | Out-Null +} catch { + if (! (Test-Path -Path "$MAVEN_HOME" -PathType Container)) { + Write-Error "fail to move MAVEN_HOME" + } +} finally { + try { Remove-Item $TMP_DOWNLOAD_DIR -Recurse -Force | Out-Null } + catch { Write-Warning "Cannot remove $TMP_DOWNLOAD_DIR" } +} + +Write-Output "MVN_CMD=$MAVEN_HOME/bin/$MVN_CMD" diff --git a/pom.xml b/pom.xml index 412693a..e972cfe 100644 --- a/pom.xml +++ b/pom.xml @@ -4,20 +4,41 @@ org.microbean microbean-bean - 0.0.11-SNAPSHOT - - - org.microbean - microbean-pluginmanagement-pom - 21 - - + 0.0.4-SNAPSHOT microBean™ Bean microBean™ Bean: Utility classes for implementing beans. 2023 https://microbean.github.io/microbean-bean + + microBean™ + http://microbean.systems/ + + + + + The Apache License, Version 2.0 + repo + Apache License 2.0 + https://www.apache.org/licenses/LICENSE-2.0.txt + + + + + + ljnelson + Laird Nelson + ljnelson@gmail.com + https://about.me/lairdnelson + + architect + developer + + -8 + + + ${scm.url} ${scm.url} @@ -25,6 +46,11 @@ HEAD + + Github + https://github.com/microbean/microbean-bean/issues + + sonatype-oss-repository-hosting @@ -36,14 +62,73 @@ microBean™ Bean Site https://microbean.github.io/microbean-bean/ + + sonatype-oss-repository-hosting + https://oss.sonatype.org/content/repositories/snapshots + + + + ${project.basedir}/src/test/java/logging.properties + + + 23 + true + true + + + ${project.organization.name}. All rights reserved.]]> + <a href="${project.url}" target="_top"><span style="font-family:Lobster, cursive;">µb</span> ${project.artifactId}</a> ${project.version} + https://microbean.github.io/microbean-construct/apidocs/,https://microbean.github.io/microbean-qualifier/apidocs/,https://microbean.github.io/microbean-scope/apidocs/ + 2 + + + + deploy,post-site,scm-publish:publish-scm + deployment + [maven-release-plugin] [skip ci] + v@{project.version} + false + + + ${project.reporting.outputDirectory} + ${project.scm.developerConnection} + gh-pages + + + true + false + + + + true + + https://oss.sonatype.org/ + 10 + + + UTF8 + UTF8 + scm:git:git@github.com:microbean/microbean-bean.git + + + + + + - - + + org.junit + junit-bom + 5.11.3 + pom + import + @@ -54,20 +139,18 @@ import - - - org.junit.jupiter - junit-jupiter-params - 5.10.2 + org.microbean + microbean-constant + 0.0.7 org.microbean - microbean-constant - 0.0.7 + microbean-construct + 0.0.6 @@ -85,7 +168,7 @@ org.microbean microbean-lang - 0.0.20 + 0.0.21 @@ -100,16 +183,11 @@ 0.0.3 - - - - - org.microbean microbean-constant @@ -118,19 +196,19 @@ org.microbean - microbean-invoke + microbean-construct compile org.microbean - microbean-interceptor + microbean-invoke compile org.microbean - microbean-lang + microbean-interceptor compile @@ -146,10 +224,8 @@ compile - - org.jboss.weld.se weld-se-core @@ -174,18 +250,24 @@ test - - + + maven-antrun-plugin + 3.1.0 + + + maven-assembly-plugin + 3.7.1 + maven-checkstyle-plugin - 3.3.1 + 3.6.0 - + @@ -293,58 +375,158 @@ - + + maven-clean-plugin + 3.4.0 + + + + ${basedir} + + src/**/*~ + *~ + + + + + maven-compiler-plugin - 3.12.1 + 3.13.0 -Xlint:all - -Xpkginfo:always + -parameters + + + org.codehaus.plexus + plexus-java + 1.3.0 + + + + + maven-dependency-plugin + 3.8.1 + + + maven-deploy-plugin + 3.1.3 + + + maven-enforcer-plugin + 3.5.0 + + + maven-gpg-plugin + + 3.2.7 + + + maven-install-plugin + 3.1.3 + + + maven-jar-plugin + 3.4.2 - maven-javadoc-plugin + 3.11.2 - - --add-stylesheet - ${project.basedir}/src/main/javadoc/css/fonts.css - true + + + microbean.idempotency + m + Idempotency: + + + microbean.nullability + cfm + Nullability: + + + microbean.threadsafety + fmpt + Thread Safety: + + - + + maven-plugin-plugin + 3.9.0 + + + maven-project-info-reports-plugin + 3.8.0 + + + maven-release-plugin + + 3.1.1 + + + maven-resources-plugin + 3.3.1 + + + maven-scm-plugin + 2.1.0 + + + maven-scm-publish-plugin + 3.3.0 + + + maven-site-plugin + 3.21.0 + + + maven-source-plugin + 3.3.1 + + + attach-sources + + jar-no-fork + + + + maven-surefire-plugin + 3.5.2 + + + org.apache.maven.surefire + surefire-junit-platform + 3.5.2 + + + + + maven-toolchains-plugin + 3.2.0 + + + com.github.spotbugs + spotbugs-maven-plugin + 4.8.6.6 + + + org.codehaus.mojo + versions-maven-plugin + 2.18.0 + + + io.smallrye + jandex-maven-plugin 3.2.3 - - - --add-exports jdk.compiler/com.sun.tools.javac.comp=org.microbean.lang - --add-exports jdk.compiler/com.sun.tools.javac.model=org.microbean.lang - --add-opens jdk.compiler/com.sun.tools.javac.model=org.microbean.lang - - - - - - junit.jupiter.execution.parallel.enabled=true - junit.jupiter.execution.parallel.mode.default=concurrent - junit.jupiter.testclass.order.default=org.junit.jupiter.api.ClassOrderer$Random - junit.jupiter.testmethod.order.default=org.junit.jupiter.api.MethodOrderer$Random - junit.platform.stacktrace.pruning.enabled=true - - - - src/test/java/logging.properties - ${org.microbean.lang.Lang.verbose} - ${project.build.directory} - ${project.build.testOutputDirectory} - - - org.sonatype.plugins nexus-staging-maven-plugin @@ -364,49 +546,72 @@ ${autoReleaseAfterClose} - - - - - - - openjdk - - - - true - - https://oss.sonatype.org/ - 10 - - - 21 - 21 - 21 - - - false - https://docs.oracle.com/en/java/javase/21/docs/api/,https://microbean.github.io/microbean-lang/apidocs/,https://microbean.github.io/microbean-qualifier/apidocs/,https://microbean.github.io/microbean-scope/apidocs/ - - - deploy,post-site,scm-publish:publish-scm - - scm:git:git@github.com:microbean/microbean-bean.git - - ${project.reporting.outputDirectory} - ${project.scm.developerConnection} - gh-pages - - false + + + maven-enforcer-plugin + + + enforce-maven + + enforce + + + + + 23 + + + 3.9.9 + + + + + + + + maven-surefire-plugin + + + + + junit.jupiter.execution.parallel.enabled=true + junit.jupiter.execution.parallel.mode.default=concurrent + + + + ${java.util.logging.config.file} + ${project.build.directory} + ${project.build.testOutputDirectory} + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + + - + + + src/main/resources + + + + META-INF + ${project.basedir} + + LICENSE + + + + + maven-javadoc-plugin @@ -430,4 +636,41 @@ + + + deployment + + + + maven-gpg-plugin + + + sign-artifacts + verify + + sign + + + + + + maven-javadoc-plugin + + + attach-javadocs + + jar + + + + + + maven-source-plugin + + + + + + + diff --git a/src/main/java/module-info.java b/src/main/java/module-info.java index 27f8427..6dfd611 100644 --- a/src/main/java/module-info.java +++ b/src/main/java/module-info.java @@ -23,8 +23,9 @@ requires transitive java.compiler; requires org.microbean.constant; + requires transitive org.microbean.construct; requires transitive org.microbean.interceptor; - requires transitive org.microbean.lang; + // requires transitive org.microbean.lang; requires transitive org.microbean.qualifier; requires transitive org.microbean.scope; diff --git a/src/main/java/org/microbean/bean/AbstractTypeMatcher.java b/src/main/java/org/microbean/bean/AbstractTypeMatcher.java new file mode 100644 index 0000000..3ba73e9 --- /dev/null +++ b/src/main/java/org/microbean/bean/AbstractTypeMatcher.java @@ -0,0 +1,427 @@ +/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- + * + * 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 + * + * 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.bean; + +import java.lang.constant.ClassDesc; +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.Collections; +import java.util.List; +import java.util.Objects; +import java.util.Optional; + +import java.util.function.Predicate; + +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.Parameterizable; +import javax.lang.model.element.QualifiedNameable; + +import javax.lang.model.type.ArrayType; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.IntersectionType; +import javax.lang.model.type.ReferenceType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.TypeVariable; +import javax.lang.model.type.WildcardType; + +import org.microbean.construct.Domain; + +import org.microbean.construct.type.UniversalType; + +import static java.lang.constant.ConstantDescs.BSM_INVOKE; + +public abstract class AbstractTypeMatcher implements Constable { + + + private static final ClassDesc CD_Domain = ClassDesc.of("org.microbean.construct.Domain"); + + + /* + * Instance fields. + */ + + + private final Domain domain; + + + /* + * Constructors. + */ + + + protected AbstractTypeMatcher(final Domain domain) { + super(); + this.domain = domain; + } + + + /* + * Instance methods. + */ + + + // Is payload's condensed super bound (lower bound) covariantly assignable to receiver? + // + // Since either of a wildcard type's bounds may be a type variable, and since a type variable's bound may be an + // intersection type, it follows that after condensation either of a wildcard type's bounds may be a list of actual + // types. + // + // Therefore this is really: Is there one bound among payload's actual super bounds that is assignable to receiver? + /* + protected boolean assignableFromCondensedSuperBound(final ReferenceType receiver, final WildcardType payload) { + assert payload.getKind() == TypeKind.WILDCARD; + final ReferenceType superBound = (ReferenceType)payload.getSuperBound(); + return superBound == null || switch (superBound.getKind()) { + case ARRAY, DECLARED, TYPEVAR -> covariantlyAssignable(receiver, superBound); + // case TYPEVAR -> covariantlyAssignable(receiver, (ReferenceType)this.types.condense(superBound)); + default -> throw new AssertionError(); + }; + } + */ + + // Is candidate covariantly assignable to w's condensed extends (upper) bound? + /* + protected boolean assignableToCondensedExtendsBound(final WildcardType w, final ReferenceType candidate) { + assert w.getKind() == TypeKind.WILDCARD; + final ReferenceType extendsBound = (ReferenceType)w.getExtendsBound(); + return extendsBound == null || switch (extendsBound.getKind()) { + case ARRAY, DECLARED, TYPEVAR -> covariantlyAssignable(extendsBound, candidate); + // case TYPEVAR -> covariantlyAssignable((ReferenceType)this.types.condense(extendsBound), List.of(candidate)); + default -> throw new AssertionError(); + }; + } + */ + + // Is the type argument represented by payload assignable to all of the receiver's condensed bounds? + // + // Pay close attention to how this is called, i.e. what is payload and what is receiver is often "backwards". For + // example: + // + // @Inject Foo foo; <-- Bean // is the String "actual type" (payload) assignable to T's (receiver's) CharSequence bound? + // // ^ payload! ^ receiver! + // + // Recall that after condensing "T extends CharSequence" you get CharSequence. + // + // Recall that if, instead, you had T extends S and S extends CharSequence, condensing T still yields CharSequence. + /* + protected boolean assignableToCondensedTypeVariableBounds(final TypeVariable receiver, final TypeMirror payload) { + assert receiver.getKind() == TypeKind.TYPEVAR; + return this.covariantlyAssignable(receiver, payload); + // return covariantlyAssignable(List.of(receiver), List.of(payload)); // deliberately List.of(payload) and not condense(payload) + } + */ + + protected boolean contains(final TypeMirror t, final TypeMirror s) { + return this.domain.contains(t, s); + } + + // It's not immediately clear what CDI means by a type variable's upper bound. In javax.lang.model.type parlance, the + // upper bound of a TypeVariable could be an IntersectionType, which java.lang.reflect.TypeVariable represents as a + // collection of bounds. CDI blundered into this earlier: https://issues.redhat.com/browse/CDI-440 and + // https://github.com/jakartaee/cdi/issues/682 + // + // In what follows, note the strangeness as well of the reversal of assignment semantics. The *receiver* gets checked + // to see if it is assignable *to* the *payload*. This method itself is not responsible for those semantics. + // + // "the upper bound of the required type parameter [receiver type argument] is assignable to the upper bound, if any, + // of the bean type parameter [payload type argument]" (when both arguments are type variables) should *actually* + // read: + // + // "for each bound, PTA, of the bean type parameter [payload type argument], there is at least one bound, RTA, of the + // required type parameter [receiver type argument], which is assignable to PTA." + // + // (Spin through all PTAs, and for each PTA: + // (Spin through all RTAs, and find one assignable to PTA.) + // + // The TCK enforces this, even though it's not in the specification (!). + // + // Weld's implementation confuses type parameters with arguments, just like the specification. They have a series of + // methods implemented as part of PR 614 (https://github.com/weld/core/pull/614) named "parametersMatch" [arguments + // match]. + // + // Weld also has methods named things like "getUppermostTypeVariableBounds" and "getUppermostBounds". These appear to + // "condense" useless type variable extensions to "get to" the "real" types involved. So T extends S extends String + // becomes String. + // + // Digging deeper, you (I) might think getUppermostTypeVariableBounds(tv) is just erase(tv) applied recursively. But I + // think you would be wrong. + // + // Start with: + // https://github.com/openjdk/jdk/blob/181845ae46157a9bb3bf8e2a328fa59eddc0273a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java#L2450 + // + // Compare vs.: + // https://github.com/weld/core/blob/e894d1699ff1c91332605f5ecae5f53410effb81/impl/src/main/java/org/jboss/weld/resolution/AbstractAssignabilityRules.java#L57-L62 + // + // To illustrate the difference between the two operations, recursive erasure (according to the Java Language + // Specification) of the pseudo-declaration T extends S extends List & Serializable would yield, simply, List + // (T would erease to "the erasure of its leftmost bound", which would be the erasure of S, which would be "the + // erasure of its leftmost bound", which would be the erasure of List, which would be List. Serializable just + // gets dropped.) + // + // By contrast, Weld's getUppermostTypeVariableBounds(T) operation would yield [List, Serializable] (have not + // tested this with code, just reading). + // + // I think this is what the javac compiler calls, somewhat confusingly, "classBound": + // https://github.com/openjdk/jdk/blob/jdk-24%2B7/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java#L2760-L2796 + // + // (With the javax.lang.model.type.* model, this condensing isn't needed.) + // So then: + // + // For every bound in (condensed) receiverBounds, is there a bound in (condensed) payloadBounds that is covariantly + // assignable to it? + // + // Note that the "backwards" nature of that sentence has already happened before we get to this method. + // + // (Is there one bound in (condensed) payloadBounds that matches all bounds in (condensed) receiverBounds?) + // + // (Because of the javax.lang.model.* isAssignable() semantics, condensing turns out to be entirely unnecessary.) + protected boolean covariantlyAssignable(final List receiverBounds, List payloadBounds) { + for (final TypeMirror receiverBound : receiverBounds) { + if (!covariantlyAssignable(receiverBound, payloadBounds)) { + return false; + } + } + return true; + /* + payloadBounds = this.types.condense(payloadBounds); + for (final TypeMirror receiverBound : this.types.condense(receiverBounds)) { + if (!covariantlyAssignable(receiverBound, payloadBounds)) { + return false; + } + } + return true; + */ + } + + // Is there a bound in payloadBounds that is assignable to receiver using Java, not CDI, assignability semantics? + protected boolean covariantlyAssignable(final TypeMirror receiver, final List payloadBounds) { + for (final TypeMirror payloadBound : payloadBounds) { + if (covariantlyAssignable(receiver, payloadBound)) { + return true; + } + } + return false; + /* + return switch (receiver.getKind()) { + case ARRAY, DECLARED -> { + for (final TypeMirror condensedPayloadBound : payloadBounds) { + switch (condensedPayloadBound.getKind()) { + case ARRAY: + case DECLARED: + if (covariantlyAssignable(receiver, (ReferenceType)condensedPayloadBound)) { + yield true; + } + break; + default: + throw new IllegalArgumentException("payloadBounds: " + payloadBounds); + } + } + yield false; + } + default -> throw new IllegalArgumentException("receiver: " + receiver + "; kind: " + receiver.getKind()); + }; + */ + } + + // Is classOrArrayTypePayload assignable to receiver following the rules of Java assignability + // (i.e. covariance)? + protected boolean covariantlyAssignable(final TypeMirror receiver, final TypeMirror payload) { + return Objects.requireNonNull(receiver, "receiver") == payload || this.domain().assignable(payload, receiver); // yes, "backwards" + } + + /** + * Returns an {@link Optional} housing a {@link ConstantDesc} that represents this {@link AbstractTypeMatcher} + * implementation. + * + *

This method never returns {@code null}.

+ * + *

The default implementation of this method relies on the presence of a {@code public} constructor that accepts a + * single {@link Domain}-typed argument.

+ * + *

The {@link Optional} returned by an invocation of this method may be, and often will be, {@linkplain + * Optional#isEmpty() empty}.

+ * + * @return an {@link Optional} housing a {@link ConstantDesc} that represents this {@link AbstractTypeMatcher} + * implementation; never {@code null} + * + * @see Constable#describeConstable() + */ + @Override // Constable + public Optional describeConstable() { + return (this.domain() instanceof Constable c ? c.describeConstable() : Optional.empty()) + .map(domainDesc -> DynamicConstantDesc.of(BSM_INVOKE, + MethodHandleDesc.ofConstructor(ClassDesc.of(this.getClass().getName()), + CD_Domain), + domainDesc)); + } + + // Is payload "identical to" receiver, following the intent of CDI? The relation "identical to" is not defined in the + // specification. Does it mean ==? Does it mean equals()? Does it mean + // javax.lang.model.util.Types#isSameType(TypeMirror, TypeMirror)? Something else? + // + // This implementation chooses Domain#sameType(TypeMirror, TypeMirror), but with one + // change. Domain#sameType(TypeMirror, TypeMirror) is usually backed by the + // javax.lang.model.util.Types#isSameType(TypeMirror, TypeMirror) method. That method will return false if either + // argument is a wildcard type. This method first checks to see if the arguments are the same Java references (==), + // regardless of type. + protected boolean identical(final TypeMirror receiver, final TypeMirror payload) { + // CDI has an undefined notion of "identical to". This method attempts to divine and implement the intent. Recall + // that javax.lang.model.* compares types with "sameType" semantics. + return + Objects.requireNonNull(receiver, "receiver") == Objects.requireNonNull(payload, "payload") || + this.domain().sameType(receiver, payload); + } + + // Return t if its element declares a non-generic class, or if it is the raw type usage of a generic class. + protected TypeMirror nonGenericClassOrRawType(final TypeMirror t) { + return this.yieldsRawType(t) ? this.rawType(t) : t; + } + + /** + * Returns the {@link Domain} used by this {@link AbstractTypeMatcher} implementation. + * + * @return the {@link Domain} used by this {@link AbstractTypeMatcher} implementation; never {@code + * null} + * + * @see #AbstractTypeMatcher(Domain) + * + * @see Domain + */ + protected final Domain domain() { + return this.domain; + } + + protected final TypeMirror elementType(final TypeMirror t) { + return this.domain().elementType(t); + } + + protected final boolean generic(final TypeMirror t) { + final Domain domain = this.domain(); + return domain.generic(domain.asElement(t)); + } + + protected final boolean parameterized(final TypeMirror t) { + return this.domain().parameterized(t); + } + + protected final boolean raw(final TypeMirror t) { + return this.domain().raw(t); + } + + protected final TypeMirror rawType(final TypeMirror t) { + return this.domain().rawType(t); + } + + // Is t an unbounded type variable? + // + // CDI does not define what an "unbounded type variable" is. This method attempts to divine and implement the intent. + // + // Since according to the Java Language Specification, all type variables have an upper bound, which in the + // pathological case is java.lang.Object, it would seem that "unbounded" means "has java.lang.Object as its upper + // bound". + // + // It is not clear in the case of T extends S whether T is an unbounded TypeVariable or not. This interpretation + // behaves as if it is. + // + // Weld seems to take the position that an unbounded type variable is one that has java.lang.Object as its sole upper + // bound; see + // https://github.com/weld/core/blob/5.1.2.Final/impl/src/main/java/org/jboss/weld/util/Types.java#L258. Under this + // interpretation T extends S would not be considered an unbounded type variable. Type variable bounds are erased in + // every other situation in CDI. + protected final boolean unboundedTypeVariable(TypeMirror t) { + UniversalType ut = UniversalType.of(t, this.domain()); + if (ut.getKind() == TypeKind.TYPEVAR) { + ut = ut.getUpperBound(); + return this.domain().javaLangObject(ut) || this.unboundedTypeVariable(ut); + } + return false; + /* + return switch (t.getKind()) { + case TYPEVAR -> { + final List condensedBounds = this.types.condense(((TypeVariable)t).getUpperBound()); + yield condensedBounds.size() == 1 && this.types.isJavaLangObject(condensedBounds.get(0)); + } + default -> false; + }; + */ + } + + // Can t *yield* a raw type? + // + // We say that to yield a raw type, t must be either: + // + // * a declared type with at least one type argument ("parameterized") + // * an array type with a parameterized element type + protected final boolean yieldsRawType(final TypeMirror t) { + final TypeMirror rawT = this.domain().rawType(t); + return rawT != null && rawT != t; + // return parameterized(t) || t.getKind() == TypeKind.ARRAY && parameterized(elementType(t)); + } + + + + /* + * Static methods. + */ + + + // Do all supplied TypeMirrors, when used as type arguments, pass the test represented by the supplied Predicate? + protected static final boolean allTypeArgumentsAre(final Iterable typeArguments, final Predicate p) { + for (final TypeMirror t : typeArguments) { + if (!p.test(t)) { + return false; + } + } + return true; + } + + // Is t a type that CDI considers to be "parameterized" in its own sense of the word, not the sense used by the Java + // Language Specification? + // + // There are some cases, but not all, where CDI (incorrectly) considers an array type to be something that can be + // parameterized, or else "bean" and "event type parameter" resolution would never work. See + // https://stackoverflow.com/questions/76493672/when-cdi-speaks-of-a-parameterized-type-does-it-also-incorrectly-mean-array-typ. + // + // The semantics CDI wants to express are really: can t *yield* a raw type, for a certain definition of "yield"? See + // Types#yieldsRawType(TypeMirror). + protected final boolean cdiParameterized(final TypeMirror t) { + return this.yieldsRawType(t); + } + + // Returns a new IllegalArgumentException describing an illegal payload type. + static IllegalArgumentException illegalPayload(final TypeMirror payload) { + return new IllegalArgumentException("Illegal payload kind: " + payload.getKind() + "; payload: " + payload); + } + + // Returns a new IllegalArgumentException describing an illegal receiver type. + static IllegalArgumentException illegalReceiver(final TypeMirror receiver) { + return new IllegalArgumentException("Illegal receiver kind: " + receiver.getKind() + "; receiver: " + receiver); + } + + // Regardless of its reported TypeKind, does t's declaring TypeElement bear the supplied fully qualified name? + // + // Throws ClassCastException if the return value of t.asElement() is not a TypeElement. + protected static final boolean named(final DeclaredType t, final CharSequence n) { + // (No getKind() check on purpose.) + return ((QualifiedNameable)t.asElement()).getQualifiedName().contentEquals(n); + } + + +} diff --git a/src/main/java/org/microbean/bean/AttributedElement.java b/src/main/java/org/microbean/bean/AttributedElement.java index 6d10b72..e7d0362 100644 --- a/src/main/java/org/microbean/bean/AttributedElement.java +++ b/src/main/java/org/microbean/bean/AttributedElement.java @@ -34,8 +34,6 @@ import static java.lang.constant.ConstantDescs.BSM_INVOKE; import static java.lang.constant.ConstantDescs.CD_List; -import static org.microbean.lang.ConstantDescs.CD_Element; - /** * An {@link Element} that has been decorated with {@linkplain NamedAttributeMap attributes}. * @@ -49,6 +47,8 @@ */ public final record AttributedElement(Element element, List> attributes) implements Constable { + private static final ClassDesc CD_Element = ClassDesc.of("javax.lang.model.element.Element"); + public AttributedElement { Objects.requireNonNull(element, "element"); attributes = List.copyOf(attributes); diff --git a/src/main/java/org/microbean/bean/AttributedType.java b/src/main/java/org/microbean/bean/AttributedType.java index dfca672..05287fb 100644 --- a/src/main/java/org/microbean/bean/AttributedType.java +++ b/src/main/java/org/microbean/bean/AttributedType.java @@ -31,10 +31,10 @@ import static java.lang.constant.ConstantDescs.BSM_INVOKE; import static java.lang.constant.ConstantDescs.CD_List; -import static org.microbean.lang.ConstantDescs.CD_TypeMirror; - public final record AttributedType(TypeMirror type, List> attributes) implements Constable { + private static final ClassDesc CD_TypeMirror = ClassDesc.of("javax.lang.model.type.TypeMirror"); + public AttributedType { type = switch (type.getKind()) { case ARRAY, BOOLEAN, BYTE, CHAR, DECLARED, DOUBLE, FLOAT, INT, LONG, SHORT -> type; diff --git a/src/main/java/org/microbean/bean/AutoCloseableRegistry.java b/src/main/java/org/microbean/bean/AutoCloseableRegistry.java index 2d03e21..7a4edff 100644 --- a/src/main/java/org/microbean/bean/AutoCloseableRegistry.java +++ b/src/main/java/org/microbean/bean/AutoCloseableRegistry.java @@ -1,6 +1,6 @@ /* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- * - * Copyright © 2023–2024 microBean™. + * Copyright © 2023–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 @@ -40,12 +40,12 @@ public interface AutoCloseableRegistry extends AutoCloseable { * * @exception IllegalStateException if this {@link AutoCloseableRegistry} is {@linkplain #closed() closed} * - * @nullability Implementations of this method must not return {@code null}. + * @microbean.nullability Implementations of this method must not return {@code null}. * - * @idempotency All successful invocations of implementations of this method must return new, distinct {@link + * @microbean.idempotency All successful invocations of implementations of this method must return new, distinct {@link * AutoCloseableRegistry} instances. * - * @threadsafety Implementations of this method must be safe for concurrent use by multiple threads. + * @microbean.threadsafety Implementations of this method must be safe for concurrent use by multiple threads. * * @see #register(AutoCloseable) */ @@ -58,9 +58,9 @@ public interface AutoCloseableRegistry extends AutoCloseable { *

After any successful invocation of this method, an invocation of the {@link #closed()} method must forever after * return {@code true}.

* - * @idempotency Implementations of this method must be idempotent. + * @microbean.idempotency Implementations of this method must be idempotent. * - * @threadsafety Implementations of this method must be safe for concurrent use by multiple threads. + * @microbean.threadsafety Implementations of this method must be safe for concurrent use by multiple threads. * * @see #closed() */ @@ -78,9 +78,9 @@ public interface AutoCloseableRegistry extends AutoCloseable { * * @return {@code true} if and only if this {@link AutoCloseableRegistry} is {@linkplain #close() closed} * - * @idempotency Implementations of this method must be idempotent. + * @microbean.idempotency Implementations of this method must be idempotent. * - * @threadsafety Implementations of this method must be safe for concurrent use by multiple threads. + * @microbean.threadsafety Implementations of this method must be safe for concurrent use by multiple threads. * * @see #close() */ @@ -101,9 +101,9 @@ public interface AutoCloseableRegistry extends AutoCloseable { * * @exception NullPointerException if {@code closeable} is {@code null} * - * @idempotency Implementations of this method must be idempotent. + * @microbean.idempotency Implementations of this method must be idempotent. * - * @threadsafety Implementations of this method must be safe for concurrent use by multiple threads. + * @microbean.threadsafety Implementations of this method must be safe for concurrent use by multiple threads. */ public boolean register(final AutoCloseable closeable); diff --git a/src/main/java/org/microbean/bean/BeanQualifiersMatcher.java b/src/main/java/org/microbean/bean/BeanQualifiersMatcher.java new file mode 100644 index 0000000..ca2984d --- /dev/null +++ b/src/main/java/org/microbean/bean/BeanQualifiersMatcher.java @@ -0,0 +1,53 @@ +/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- + * + * Copyright © 2024 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.bean; + +import java.util.Collection; + +import org.microbean.qualifier.NamedAttributeMap; + +import static org.microbean.bean.Qualifiers.anyAndDefaultQualifiers; +import static org.microbean.bean.Qualifiers.defaultQualifier; +import static org.microbean.bean.Qualifiers.qualifiers; + +public final class BeanQualifiersMatcher implements Matcher>, Collection>> { + + public BeanQualifiersMatcher() { + super(); + } + + @Override // Matcher>, Collection>> + public final boolean test(final Collection> receiverAttributes, + final Collection> payloadAttributes) { + final Collection> receiverQualifiers = qualifiers(receiverAttributes); + final Collection> payloadQualifiers = qualifiers(payloadAttributes); + // https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#performing_typesafe_resolution + // "A bean is assignable to a given injection point if...the bean has all the required qualifiers. If no required + // qualifiers were explicitly specified, the container assumes the required qualifier @Default." + // + // https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#builtin_qualifiers + // "Every bean has the built-in qualifier @Any, even if it does not explicitly declare this qualifier. If a bean + // does not explicitly declare a qualifier other than @Named or @Any, the bean has exactly one additional qualifier, + // of type @Default. This is called the default qualifier." + // + // https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#injection_point_default_qualifier + // "If an injection point declares no qualifier, the injection point has exactly one qualifier, the default + // qualifier @Default." + return + receiverQualifiers.isEmpty() ? payloadQualifiers.isEmpty() || payloadQualifiers.contains(defaultQualifier()) : + payloadQualifiers.isEmpty() ? anyAndDefaultQualifiers().containsAll(receiverQualifiers) : + payloadQualifiers.containsAll(receiverQualifiers); + } + +} diff --git a/src/main/java/org/microbean/bean/BeanTypeMatcher.java b/src/main/java/org/microbean/bean/BeanTypeMatcher.java new file mode 100644 index 0000000..cd03ef1 --- /dev/null +++ b/src/main/java/org/microbean/bean/BeanTypeMatcher.java @@ -0,0 +1,543 @@ +/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- + * + * Copyright © 2023–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.bean; + +import java.lang.System.Logger; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Objects; + +import java.util.function.Predicate; + +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.Parameterizable; +import javax.lang.model.element.QualifiedNameable; + +import javax.lang.model.type.ArrayType; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.IntersectionType; +import javax.lang.model.type.ReferenceType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.TypeVariable; +import javax.lang.model.type.WildcardType; + +import org.microbean.construct.Domain; + +/** + * A {@link Matcher} encapsulating CDI-compatible + * type matching rules. + * + * @author Laird Nelson + * + * @see #test(TypeMirror, TypeMirror) + */ +public final class BeanTypeMatcher extends AbstractTypeMatcher implements Matcher { + + + /* + * Static fields. + */ + + + private static final Logger LOGGER = System.getLogger(BeanTypeMatcher.class.getName()); + + + /* + * Constructors. + */ + + + /** + * Creates a new {@link BeanTypeMatcher}. + * + * @param domain a {@link Domain}; must not be {@code null} + * + * @exception NullPointerException if {@code tes} is {@code null} + * + * @see Domain + */ + public BeanTypeMatcher(final Domain domain) { + super(domain); + } + + + /* + * Instance methods. + */ + + + /* + * Public methods. + */ + + /** + * Returns {@code true} if and only if the supplied {@code payload} argument matches + * the supplied {@code receiver} argument, according to the rules defined by section + * 2.4.2.1 of the CDI specification. + * + * @param receiver the left hand side of a type assignment; must not be {@code null} + * + * @param payload the right hand side of a type assignment; must not be {@code null} + * + * @return {@code true} if and only if the supplied {@code payload} argument matches + * the supplied {@code receiver} argument, according to the rules defined by section + * 2.4.2.1 of the CDI specification; {@code false} otherwise + * + * @exception NullPointerException if either argument is {@code null} + * + * @exception IllegalArgumentException if either type is any type other than an {@linkplain TypeKind#ARRAY array + * type}, a {@link TypeKind#isPrimitive() primitive type}, or a {@linkplain TypeKind#DECLARED declared type} + */ + // Is the payload assignable to the receiver? That is, does the payload "match the receiver", in CDI parlance? + @Override // Matcher + public final boolean test(final TypeMirror receiver, final TypeMirror payload) { + // "A bean is assignable to a given injection point if: + // + // "The bean has a [legal] bean type [payload] that matches the [legal] required type [receiver]. For this + // purpose..." + return receiver == Objects.requireNonNull(payload, "payload") || switch (receiver.getKind()) { + + case ARRAY -> switch (payload.getKind()) { + // "...array types are considered to match only if their element types [note: not component types] are + // identical ['identical' is actually undefined in the specification]..." + case ARRAY -> this.identical(this.domain().elementType((ArrayType)receiver), this.domain().elementType((ArrayType)payload)); + case BOOLEAN, BYTE, CHAR, DOUBLE, FLOAT, INT, LONG, SHORT -> false; + case DECLARED -> false; + default -> throw illegalPayload(payload); + }; + + // "...primitive types are considered to match their corresponding wrapper types in java.lang..." + case BOOLEAN -> switch (payload.getKind()) { + case ARRAY -> false; + case BOOLEAN -> true; + case BYTE, CHAR, DOUBLE, FLOAT, INT, LONG, SHORT -> false; + case DECLARED -> named((DeclaredType)payload, "java.lang.Boolean"); + default -> throw illegalPayload(payload); + }; + + case BYTE -> switch (payload.getKind()) { + case ARRAY -> false; + case BYTE -> true; + case BOOLEAN, CHAR, DOUBLE, FLOAT, INT, LONG, SHORT -> false; + case DECLARED -> named((DeclaredType)payload, "java.lang.Byte"); + default -> throw illegalPayload(payload); + }; + + case CHAR -> switch (payload.getKind()) { + case ARRAY -> false; + case CHAR -> true; + case BOOLEAN, BYTE, DOUBLE, FLOAT, INT, LONG, SHORT -> false; + case DECLARED -> named((DeclaredType)payload, "java.lang.Character"); + default -> throw illegalPayload(payload); + }; + + case DOUBLE -> switch (payload.getKind()) { + case ARRAY -> false; + case DOUBLE -> true; + case BOOLEAN, BYTE, CHAR, FLOAT, INT, LONG, SHORT -> false; + case DECLARED -> named((DeclaredType)payload, "java.lang.Double"); + default -> throw illegalPayload(payload); + }; + + case FLOAT -> switch (payload.getKind()) { + case ARRAY -> false; + case FLOAT -> true; + case BOOLEAN, BYTE, CHAR, DOUBLE, INT, LONG, SHORT -> false; + case DECLARED -> named((DeclaredType)payload, "java.lang.Float"); + default -> throw illegalPayload(payload); + }; + + case INT -> switch (payload.getKind()) { + case ARRAY -> false; + case INT -> true; + case BOOLEAN, BYTE, CHAR, DOUBLE, FLOAT, LONG, SHORT -> false; + case DECLARED -> named((DeclaredType)payload, "java.lang.Integer"); + default -> throw illegalPayload(payload); + }; + + case LONG -> switch (payload.getKind()) { + case ARRAY -> false; + case LONG -> true; + case BOOLEAN, BYTE, CHAR, DOUBLE, FLOAT, INT, SHORT -> false; + case DECLARED -> named((DeclaredType)payload, "java.lang.Long"); + default -> throw illegalPayload(payload); + }; + + case SHORT -> switch (payload.getKind()) { + case ARRAY -> false; + case SHORT -> true; + case BOOLEAN, BYTE, CHAR, DOUBLE, FLOAT, INT, LONG -> false; + case DECLARED -> named((DeclaredType)payload, "java.lang.Short"); + default -> throw illegalPayload(payload); + }; + + case DECLARED -> switch (payload.getKind()) { + // Implied. + case ARRAY -> false; + + // "...primitive types are considered to match their corresponding wrapper types in java.lang..." + case BOOLEAN -> named((DeclaredType)receiver, "java.lang.Boolean"); + case BYTE -> named((DeclaredType)receiver, "java.lang.Byte"); + case CHAR -> named((DeclaredType)receiver, "java.lang.Character"); + case DOUBLE -> named((DeclaredType)receiver, "java.lang.Double"); + case FLOAT -> named((DeclaredType)receiver, "java.lang.Float"); + case INT -> named((DeclaredType)receiver, "java.lang.Integer"); + case LONG -> named((DeclaredType)receiver, "java.lang.Long"); + case SHORT -> named((DeclaredType)receiver, "java.lang.Short"); + + // "Parameterized and raw types [and non-generic classes, and non-array types]..." + case DECLARED -> + // "...are considered to match if they are identical [undefined]..." + this.identical(receiver, payload) || + // "...or if the bean type [payload] is assignable to [see #assignable(TypeMirror, TypeMirror)] the required type + // [receiver]...." + this.assignable((DeclaredType)receiver, (DeclaredType)payload); + + default -> throw illegalPayload(payload); + }; + + default -> throw illegalReceiver(receiver); + }; + } + + /* + * Private methods. + */ + + private final boolean assignable(final DeclaredType receiver, final DeclaredType payload) { + assert receiver.getKind() == TypeKind.DECLARED; + assert payload.getKind() == TypeKind.DECLARED; + return switch (payload) { + + case DeclaredType parameterizedPayload when parameterized(payload) -> switch (receiver) { + // "A parameterized bean type [parameterizedPayload] is considered assignable..." + + case DeclaredType nonGenericOrRawReceiver when !generic(receiver) || raw(receiver) -> + // "...to a [non-generic class or] raw required type [nonGenericOrRawReceiver] if the [non-generic class or] + // raw types are identical [undefined] and all type parameters [type arguments] of the bean type + // [parameterizedPayload] are either unbounded type variables [undefined] or java.lang.Object." + this.identical(this.nonGenericClassOrRawType(nonGenericOrRawReceiver), + this.nonGenericClassOrRawType(parameterizedPayload)) && + allTypeArgumentsAre(parameterizedPayload.getTypeArguments(), + ((Predicate)this::unboundedTypeVariable).or(this.domain()::javaLangObject)); + + case DeclaredType parameterizedReceiver -> { + // "...to a parameterized required type [parameterizedReceiver]..." + + if (this.identical(this.rawType(parameterizedReceiver), this.rawType(parameterizedPayload))) { + // "...if they have identical raw type [really if their declarations/elements are 'identical']..." + + final List rtas = parameterizedReceiver.getTypeArguments(); + final List ptas = parameterizedPayload.getTypeArguments(); + assert rtas.size() == ptas.size(); + for (int i = 0; i < rtas.size(); i++) { + final TypeMirror rta = rtas.get(i); + final TypeMirror pta = ptas.get(i); + // "...and for each parameter [type argument pair] the required type parameter [receiver type argument, + // rta] and the bean type parameter [payload type argument, pta]..." + + switch (rta.getKind()) { + case ARRAY: // rta + case DECLARED: // rta + switch (pta.getKind()) { + + case ARRAY: // pta + case DECLARED: // pta + // "...are actual types [non-type variable, non-wildcard reference types]..." + // + // CDI mentions actual types but does not define what they are. This method attempts to divine and + // implement the intent. + // + // A comment in a closed bug report (CDI-502) + // (https://issues.redhat.com/browse/CDI-502?focusedId=13036118&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-13036118) + // by one of the reference implementation's authors (Jozef Hartinger) provides the only definition: + // + // "An actual type is a type that is not a wildcard nor [sic] an unresolved [sic] type variable." + // + // The Java Language Specification does not mention anything about "actual types" or type variable + // "resolution". CDI does not mention anything about type variable "resolution". + // + // (Clearly type variable "resolution" must simply be the process of supplying a type argument for a + // type parameter.) + // + // Presumably the null type is not intended to be an actual type either. + // + // More strictly, therefore, the intent seems to be that an actual type is an array, declared or + // primitive type, and none other. + // + // See also: + // https://github.com/weld/core/blob/5.1.2.Final/impl/src/main/java/org/jboss/weld/util/Types.java#L213 + + if (this.identical(this.nonGenericClassOrRawType(rta), this.nonGenericClassOrRawType(pta))) { + // "...with identical [non-generic classes or] raw types[s], and, if the type [?] is parameterized + // [?]..." + // + // For a maximally illustrative example, let rta and pta be array types with parameterized element + // types, such as List[] and List[]. Then per the Java Language Specification (JLS) + // their raw types are List[] and List[], and their parameterized element types are List and + // List. According to the JLS, neither List[] nor List[] is parameterized. + // + // In this example, if we get here, we have only proven that List[] is "identical to" List[]. + // + // The "if the type is parameterized" clause is tough. Which type is under discussion? "the type" + // seems to refer to the "identical raw type". But a raw type by definition is not parameterized, so + // this clause would seem to be superfluous and thus never apply, and so List[] := + // List[] is OK. Oops. So not that interpretation. + // + // What if instead the "if the type is parameterized" clause means the receiver type itself and + // somehow loosely the payload type as well? Well, clearly it cannot correctly do this, since an + // array type is never a parameterized type. Same bad result. Oops. So not that interpretation. + // + // Or what if "identical raw type" really means "identical raw type (or identical component type if + // they are array types)"? That would be most efficient, since it would rule out List[] := + // List[] right off the bat: we wouldn't even get here. But that really doesn't seem to be + // what is meant. So probably not that interpretation. + // + // What if the "if the type is parameterized" clause really means "if the element type declaration + // used by both the receiver and payload types is generic"? That would work. That's also + // semantically equivalent to something like: "...if at least one of the two arguments is a + // parameterized type [e.g. List, not List[], not String, not List], or at least one + // of the two types is an array type with a parameterized element type [e.g. List[], not + // List[], not String[]]..." + // + // That is the interpretation we apply here. So: + // + // "...and, if the type [?] is parameterized [?]..." + // + // becomes: + // + // "...and, if at least one of the two type arguments is a parameterized type, or if at least one + // of the two types is an array type with a parameterized element type..." + // + // That, in turn, designates any type capable of properly yielding a raw type, while ruling out + // those that can't! That means it is exactly equal to the Types#yieldsRawType(TypeMirror) method, + // and so that's what cdiParameterized(TypeMirror) returns (go see for yourself). + if (cdiParameterized(rta)) { // really just Types#yieldsRawType(rta) + assert cdiParameterized(pta); // ...because otherwise their raw types would not have been "identical" + // "...the bean type parameter [type argument] is assignable to the required type parameter [type + // argument] according to [all of] these rules [including 'matching']..." + if (test(rta, pta)) { // note recursion + continue; // or break + } + } else { + assert !cdiParameterized(pta); + continue; // yes, trust me; vetted (or break) + } + } + yield false; + + case TYPEVAR: // pta + // "...the required type parameter [receiver type argument, rta] is an actual type [an array or + // declared type], the bean type parameter [payload type argument, pta] is a type variable, and the + // actual type [rta] is assignable to the upper bound, if any, of the type variable [pta]..." + /* + if (assignableToCondensedTypeVariableBounds((TypeVariable)pta, rta)) { + // + // (This is weird. Yes, this is "backwards"; the *receiver* type argument is being tested to see if + // it is assignable to the *payload* type argument.) + // + // TODO: I think this is fully handled by covariantlyAssignable(pta, rta) + continue; + } + */ + if (this.covariantlyAssignable(pta, rta)) { + continue; + } + yield false; + + case WILDCARD: // pta + throw illegalPayload(payload); // bean types can't have wildcard type arguments + + default: // pta + throw new AssertionError(); + } + + case TYPEVAR: // rta + switch (pta.getKind()) { + + case ARRAY: // pta + case DECLARED: // pta + yield false; // implied + + case TYPEVAR: // pta + // "...the required type parameter [receiver type argument, rta] and the bean type parameter [payload + // type argument, pta] are both type variables, and the upper bound of the required type parameter + // [rta] [a type variable has many bounds?] is assignable to the upper bound [a type variable has many + // bounds?], if any, of the bean type parameter [pta]..." + /* + if (condensedTypeVariableBoundsAssignableToCondensedTypeVariableBounds((TypeVariable)pta, (TypeVariable)rta)) { + // + // (This is weird. Yes, the *receiver* type argument is being tested to see if it is assignable to + // the *payload* type argument.) + // + // TODO: I think this is fully handled by covariantlyAssignable(pta, rta). + continue; + } + */ + if (this.covariantlyAssignable(pta, rta)) { + continue; + } + yield false; + + case WILDCARD: // pta + throw illegalPayload(payload); // bean types can't have wildcard type arguments + + default: // pta + throw new AssertionError(); + } + + case WILDCARD: // rta + // "...the required type parameter [receiver type argument, rta] is a wildcard..." + switch (pta.getKind()) { + + case ARRAY: // pta + case DECLARED: // pta + // "...the bean type parameter [payload type argument, pta] is an actual type [a non-type variable, + // non-wildcard reference type], and the actual type [pta] is assignable to the upper bound, if any, + // of the wildcard [receiver type argument, rta] and assignable from the lower bound, if any of the + // wildcard [rta]..." + /* + if (assignableToCondensedExtendsBound((WildcardType)rta, (ReferenceType)pta) && + assignableFromCondensedSuperBound((ReferenceType)pta, (WildcardType)rta)) { + // TODO: is this simply the contains() relationship? So basically: does rta contain pta? + continue; // (or break) + } + */ + if (this.contains(rta, pta)) { + continue; + } + yield false; + + case TYPEVAR: // pta + // "...the required type parameter [receiver type argument, rta] is a wildcard, the bean type + // parameter [payload type argument, pta] is a type variable, and the upper bound of the type variable + // [a type variable has many bounds!] [pta] is assignable to or assignable from the upper bound, if + // any, of the wildcard [rta] and assignable from the lower bound, if any, of the wildcard [rta]" + /* + if ((condensedTypeVariableBoundsAssignableToCondensedExtendsBound((WildcardType)rta, (TypeVariable)pta) || + condensedTypeVariableBoundsAssignableFromCondensedExtendsBound((TypeVariable)pta, (WildcardType)rta)) && + condensedTypeVariableBoundsAssignableFromCondensedSuperBound((TypeVariable)pta, (WildcardType)rta)) { + // TODO: is this simply the contains() relationship? So basically: does rta contain pta? + continue; + } + */ + if (this.contains(rta, pta)) { + continue; + } + yield false; + + case WILDCARD: // pta + throw illegalPayload(payload); // bean types can't have wildcard type arguments + + default: // pta + throw new AssertionError(); + } + + default: // rta + throw new AssertionError(); + } + + } // end type argument for loop + yield true; // we passed all the type argument assignability tests + + } // end this.identical() check + yield false; // the type arguments' non-generic classes or raw types were not identical + } + }; + + case DeclaredType nonGenericOrRawPayload -> switch (receiver) { + // "A [non-generic or] raw bean type [nonGenericOrRawPayload] is considered assignable..." + + case DeclaredType parameterizedReceiver when parameterized(receiver) -> + // "...to a parameterized required type [parameterizedReceiver] if the[ir] [non-generic classes or] raw types + // are identical and all type parameters [type arguments] of the required type [parameterizedReceiver] are + // either unbounded type variables [undefined] or java.lang.Object." + this.identical(this.nonGenericClassOrRawType(parameterizedReceiver), nonGenericOrRawPayload) && + allTypeArgumentsAre(parameterizedReceiver.getTypeArguments(), + ((Predicate)this::unboundedTypeVariable).or(this.domain()::javaLangObject)); + + // [Otherwise the payload is not assignable to the receiver; identity checking should have already happened in + // test(), not here.] + case DeclaredType nonGenericOrRawReceiver -> false; // or this.identical(nonGenericOrRawReceiver, nonGenericOrRawPayload); + }; + }; + } + + // Are payload's condensed bounds assignable to receiver's condensed extends bound (upper bound)? + /* + private final boolean condensedTypeVariableBoundsAssignableToCondensedExtendsBound(final WildcardType receiver, final TypeVariable payload) { + assert receiver.getKind() == TypeKind.WILDCARD; + assert payload.getKind() == TypeKind.TYPEVAR; + // "...the upper bound of the type variable [a type variable has many bounds?] is assignable TO [...] the upper + // bound, if any, of the wildcard..." + final TypeMirror extendsBound = receiver.getExtendsBound(); + // No need to condense arguments to eliminate useless type variables and intersection types so that Java covariant + // semantics will work properly in this case; #covariantlyAssignable(List, List) does this already. + return extendsBound == null || this.covariantlyAssignable(List.of((ReferenceType)extendsBound), List.of(payload)); + } + */ + + // Is payload's condensed extends bound (upper bound) covariantly assignable to receiver's condensed bounds? + /* + private final boolean condensedTypeVariableBoundsAssignableFromCondensedExtendsBound(final TypeVariable receiver, final WildcardType payload) { + assert receiver.getKind() == TypeKind.TYPEVAR; + assert payload.getKind() == TypeKind.WILDCARD; + // "...the upper bound of the type variable [a type variable has many bounds?] is assignable [...] FROM the upper + // bound, if any, of the wildcard..." + final TypeMirror extendsBound = payload.getExtendsBound(); + // No need to condense arguments to eliminate useless type variables and intersection types so that Java covariant + // semantics will work properly in this case; #covariantlyAssignable(List, List) does this already. + return extendsBound == null || this.covariantlyAssignable(List.of(receiver), List.of((ReferenceType)extendsBound)); + } + */ + + // Is payload's super bound (lower bound) covariantly assignable to receiver's condensed bounds? + /* + private final boolean condensedTypeVariableBoundsAssignableFromCondensedSuperBound(final TypeVariable receiver, final WildcardType payload) { + assert receiver.getKind() == TypeKind.TYPEVAR; + assert payload.getKind() == TypeKind.WILDCARD; + final TypeMirror superBound = payload.getSuperBound(); + // No need to condense arguments to eliminate useless type variables and intersection types so that Java covariant + // semantics will work properly in this case; #covariantlyAssignable(List, List) does this already. + return superBound == null || this.covariantlyAssignable(List.of(receiver), List.of(superBound)); + } + */ + + // Are payload's condensed bounds covariantly assignable to receiver's condensed bounds? + /* + private final boolean condensedTypeVariableBoundsAssignableToCondensedTypeVariableBounds(final TypeVariable receiver, final TypeVariable payload) { + assert receiver.getKind() == TypeKind.TYPEVAR; + assert payload.getKind() == TypeKind.TYPEVAR; + // No need to condense arguments to eliminate useless type variables and intersection types so that Java covariant + // semantics will work properly in this case; #covariantlyAssignable(List, List) does this already. + return this.covariantlyAssignable(List.of(receiver), List.of(payload)); + } + */ + + + /* + * Static methods. + */ + + +} diff --git a/src/main/java/org/microbean/bean/BeanTypes.java b/src/main/java/org/microbean/bean/BeanTypes.java index 7c3e11f..9063930 100644 --- a/src/main/java/org/microbean/bean/BeanTypes.java +++ b/src/main/java/org/microbean/bean/BeanTypes.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 @@ -15,45 +15,25 @@ import java.lang.System.Logger; -import java.lang.constant.ClassDesc; -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.Comparator; import java.util.List; import java.util.Map; import java.util.Objects; -import java.util.Optional; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.function.Predicate; -import javax.lang.model.element.Element; -import javax.lang.model.element.QualifiedNameable; - import javax.lang.model.type.ArrayType; import javax.lang.model.type.DeclaredType; -import javax.lang.model.type.IntersectionType; import javax.lang.model.type.TypeKind; import javax.lang.model.type.TypeMirror; -import javax.lang.model.type.TypeVariable; - -import org.microbean.lang.TypeAndElementSource; - -import static java.lang.System.Logger.Level.WARNING; - -import static java.lang.constant.ConstantDescs.BSM_INVOKE; -import static java.util.HashSet.newHashSet; +import org.microbean.construct.Domain; -import static java.util.stream.Stream.concat; +import org.microbean.construct.type.UniversalType; -import static org.microbean.lang.ConstantDescs.CD_TypeAndElementSource; +import static java.lang.System.Logger.Level.WARNING; /** * A utility for working with bean types. @@ -64,7 +44,7 @@ * * @see #legalBeanType(TypeMirror) */ -public final class BeanTypes implements Constable { +public final class BeanTypes extends Types { /* @@ -80,11 +60,7 @@ public final class BeanTypes implements Constable { */ - private final Map> beanTypesCache; - - private final Comparator c; - - private final TypeAndElementSource tes; + private final Map> beanTypesCache; /* @@ -95,12 +71,10 @@ public final class BeanTypes implements Constable { /** * Creates a new {@link BeanTypes}. * - * @param tes a {@link TypeAndElementSource}; must not be {@code null} + * @param domain a {@link Domain}; must not be {@code null} */ - public BeanTypes(final TypeAndElementSource tes) { - super(); - this.tes = Objects.requireNonNull(tes, "tes"); - this.c = new SpecializationComparator(); + public BeanTypes(final Domain domain) { + super(domain); this.beanTypesCache = new ConcurrentHashMap<>(); } @@ -109,41 +83,6 @@ public BeanTypes(final TypeAndElementSource tes) { * Instance methods. */ - /** - * Returns an {@link Optional} housing a {@link ConstantDesc} that represents this {@link BeanTypes}. - * - *

This method never returns {@code null}.

- * - *

The default implementation of this method relies on the presence of a {@code public} constructor that accepts a - * single {@link TypeAndElementSource}-typed argument.

- * - *

The {@link Optional} returned by an invocation of this method may be, and often will be, {@linkplain - * Optional#isEmpty() empty}.

- * - * @return an {@link Optional} housing a {@link ConstantDesc} that represents this {@link BeanTypes}; never {@code - * null} - * - * @see Constable#describeConstable() - */ - @Override // Constable - public final Optional describeConstable() { - return (this.tes instanceof Constable c ? c.describeConstable() : Optional.empty()) - .map(tesDesc -> DynamicConstantDesc.of(BSM_INVOKE, - MethodHandleDesc.ofConstructor(ClassDesc.of(this.getClass().getName()), - CD_TypeAndElementSource), - tesDesc)); - } - - /** - * Clears caches that may be used internally by this {@link BeanTypes}. - * - * @idempotency This method may clear internal state but otherwise has no side effects. - * - * @threadsafety This method is safe for concurrent use by multiple threads. - */ - public final void clearCaches() { - this.beanTypesCache.clear(); - } /** * Returns an immutable {@link List} of {@linkplain #legalBeanType(TypeMirror) legal bean types} that the supplied @@ -158,62 +97,71 @@ public final void clearCaches() { * * @exception NullPointerException if {@code t} is {@code null} * - * @nullability This method never returns {@code null}. + * @microbean.nullability This method never returns {@code null}. * - * @idempotency This method is idempotent and returns determinate values. + * @microbean.idempotency This method is idempotent and returns determinate values. * - * @threadsafety This method is safe for concurrent use by multiple threads. + * @microbean.threadsafety This method is safe for concurrent use by multiple threads. */ - public final List beanTypes(final TypeMirror t) { + public final List beanTypes(final TypeMirror t) { + final UniversalType ut = UniversalType.of(t, this.domain()); // https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#assignable_parameters // https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#legal_bean_types // https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#managed_bean_types // https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#producer_field_types // https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#producer_method_types - return switch (t.getKind()) { - case ARRAY -> this.beanTypesCache.computeIfAbsent(t, t0 -> legalBeanType(t0) ? List.of(t0, tes.declaredType("java.lang.Object")) : List.of()); - case BOOLEAN, BYTE, CHAR, DOUBLE, FLOAT, INT, LONG, SHORT -> this.beanTypesCache.computeIfAbsent(t, t0 -> List.of(t0, tes.declaredType("java.lang.Object"))); - case DECLARED, TYPEVAR -> this.beanTypesCache.computeIfAbsent(t, t0 -> supertypes(t0, BeanTypes::legalBeanType)); + return switch (ut.getKind()) { + case ARRAY -> this.beanTypesCache.computeIfAbsent(ut, t0 -> legalBeanType(t0) ? List.of(t0, this.domain().javaLangObject().asType()) : List.of()); + case BOOLEAN, BYTE, CHAR, DOUBLE, FLOAT, INT, LONG, SHORT -> this.beanTypesCache.computeIfAbsent(ut, t0 -> List.of(t0, this.domain().javaLangObject().asType())); + case DECLARED, TYPEVAR -> this.beanTypesCache.computeIfAbsent(ut, t0 -> this.supertypes(t0, this::legalBeanType)); default -> { - assert !legalBeanType(t); + assert !legalBeanType(ut); yield List.of(); } }; } - final List supertypes(final TypeMirror t) { - return this.supertypes(t, BeanTypes::returnTrue); - } - - private final List supertypes(final TypeMirror t, final Predicate p) { - final ArrayList nonInterfaceTypes = new ArrayList<>(7); // arbitrary size - final ArrayList interfaceTypes = new ArrayList<>(17); // arbitrary size - supertypes(t, p, nonInterfaceTypes, interfaceTypes, newHashSet(13)); // arbitrary size - nonInterfaceTypes.trimToSize(); - interfaceTypes.trimToSize(); - return - concat(nonInterfaceTypes.stream(), // non-interface supertypes are already sorted from most-specific to least - interfaceTypes.stream().sorted(this.c)) // have to sort interfaces because you can extend them in any order - .toList(); + /** + * Clears caches that may be used internally by this {@link BeanTypes}. + * + * @microbean.idempotency This method may clear internal state but otherwise has no side effects. + * + * @microbean.threadsafety This method is safe for concurrent use by multiple threads. + */ + public final void clearCaches() { + this.beanTypesCache.clear(); } - private final void supertypes(final TypeMirror t, - final Predicate p, - final ArrayList nonInterfaceTypes, - final ArrayList interfaceTypes, - final Set seen) { - if (seen.add(name(t))) { - if (p.test(t)) { - if (isInterface(t)) { - interfaceTypes.add(t); // reflexive - } else { - nonInterfaceTypes.add(t); // reflexive - } - } - for (final TypeMirror directSupertype : tes.directSupertypes(t)) { - this.supertypes(directSupertype, p, nonInterfaceTypes, interfaceTypes, seen); // note recursion - } - } + /** + * Returns {@code true} if and only if the supplied {@link TypeMirror} is a legal bean type. + * + *

Legal bean types are, exactly:

+ * + *
    + * + *
  1. {@linkplain TypeKind#ARRAY Array} types whose {@linkplain ArrayType#getComponentType() component type}s are + * legal bean types
  2. + * + *
  3. {@linkplain TypeKind#isPrimitive() Primitive} types
  4. + * + *
  5. {@linkplain TypeKind#DECLARED Declared} types that contain no {@linkplain TypeKind#WILDCARD wildcard type}s for + * every level of containment
  6. + * + *
+ * + * @param t a {@link TypeMirror}; must not be {@code null} + * + * @return {@code true} if and only if {@code t} is a legal bean type; {@code false} otherwise + * + * @exception NullPointerException if {@code t} is {@code null} + * + * @microbean.idempotency This method is idempotent and deterministic. + * + * @microbean.threadsafety This method itself is safe for concurrent use by multiple threads, but {@link TypeMirror} + * implementations and {@link Domain} implementations may not be safe for such use. + */ + public final boolean legalBeanType(final TypeMirror t) { + return legalBeanType(this.domain(), t); } @@ -221,7 +169,7 @@ private final void supertypes(final TypeMirror t, * Static methods. */ - + /** * Returns {@code true} if and only if the supplied {@link TypeMirror} is a legal bean type. * @@ -239,30 +187,65 @@ private final void supertypes(final TypeMirror t, * * * + * @param domain a {@link Domain} from which the supplied {@link TypeMirror} is presumed to have originated; must not + * be {@code null} + * * @param t a {@link TypeMirror}; must not be {@code null} * * @return {@code true} if and only if {@code t} is a legal bean type; {@code false} otherwise * - * @exception NullPointerException if {@code t} is {@code null} + * @exception NullPointerException if either {@code domain} or {@code t} is {@code null} + * + * @microbean.idempotency This method is idempotent and deterministic. + * + * @microbean.threadsafety This method itself is safe for concurrent use by multiple threads, but {@link TypeMirror} + * implementations and {@link Domain} implementations may not be safe for such use. + */ + public static final boolean legalBeanType(final Domain domain, final TypeMirror t) { + return legalBeanType(UniversalType.of(t, domain)); + } + + /** + * Returns {@code true} if and only if the supplied {@link UniversalType} is a legal bean type. + * + *

Legal bean types are, exactly:

+ * + *
    + * + *
  1. {@linkplain TypeKind#ARRAY Array} types whose {@linkplain ArrayType#getComponentType() component type}s are + * legal bean types
  2. + * + *
  3. {@linkplain TypeKind#isPrimitive() Primitive} types
  4. + * + *
  5. {@linkplain TypeKind#DECLARED Declared} types that contain no {@linkplain TypeKind#WILDCARD wildcard type}s for + * every level of containment
  6. + * + *
+ * + * @param ut a {@link UniversalType}; must not be {@code null} + * + * @return {@code true} if and only if {@code ut} is a legal bean type; {@code false} otherwise + * + * @exception NullPointerException if {@code ut} is {@code null} * - * @idempotency This method is idempotent and deterministic. + * @microbean.idempotency This method is idempotent and deterministic. * - * @threadsafety This method itself is safe for concurrent use by multiple threads, but {@link TypeMirror} - * implementations and {@link TypeAndElementSource} implementations may not be safe for such use. + * @microbean.threadsafety This method itself is safe for concurrent use by multiple threads, but {@link TypeMirror} + * implementations and {@link Domain} implementations may not be safe for such use. */ - public static final boolean legalBeanType(final TypeMirror t) { + static final boolean legalBeanType(final UniversalType ut) { // https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#assignable_parameters // https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#legal_bean_types - return switch (t.getKind()) { + return switch (ut.getKind()) { // "A bean type may be an array type." // // "However, some Java types are not legal bean types: [...] An array type whose component type is not a legal bean // type" case ARRAY -> { - if (!legalBeanType(((ArrayType)t).getComponentType())) { // note recursion + if (!legalBeanType(ut.getComponentType())) { // note recursion if (LOGGER.isLoggable(WARNING)) { - LOGGER.log(WARNING, t + " has a component type that is an illegal bean type (" + ((ArrayType)t).getComponentType() + ")"); + LOGGER.log(WARNING, ut + " has a component type that is an illegal bean type (" + ut.getComponentType() + ")"); } yield false; } @@ -287,10 +270,10 @@ public static final boolean legalBeanType(final TypeMirror t) { // // This still seems way overstrict to me but there you have it. case DECLARED -> { - for (final TypeMirror typeArgument : ((DeclaredType)t).getTypeArguments()) { - if (typeArgument.getKind() != TypeKind.TYPEVAR && !legalBeanType(typeArgument)) { // note recursion + for (final UniversalType uta : ut.getTypeArguments()) { + if (uta.getKind() != TypeKind.TYPEVAR && !legalBeanType(uta)) { // note recursion if (LOGGER.isLoggable(WARNING)) { - LOGGER.log(WARNING, t + " has a type argument that is an illegal bean type (" + typeArgument + ")"); + LOGGER.log(WARNING, ut + " has a type argument that is an illegal bean type (" + uta + ")"); } yield false; } @@ -301,95 +284,11 @@ public static final boolean legalBeanType(final TypeMirror t) { // "A type variable is not a legal bean type." (Nothing else is either.) default -> { if (LOGGER.isLoggable(WARNING)) { - LOGGER.log(WARNING, t + " is an illegal bean type"); + LOGGER.log(WARNING, ut + " is an illegal bean type"); } yield false; } }; } - static final String name(final TypeMirror t) { - return switch (t.getKind()) { - case ARRAY -> name(((ArrayType)t).getComponentType()) + "[]"; - case BOOLEAN -> "boolean"; - case BYTE -> "byte"; - case CHAR -> "char"; - case DECLARED -> name(((DeclaredType)t).asElement()); - case DOUBLE -> "double"; - case FLOAT -> "float"; - case INT -> "int"; - case INTERSECTION -> { - final java.util.StringJoiner sj = new java.util.StringJoiner("&"); - for (final TypeMirror bound : ((IntersectionType)t).getBounds()) { - sj.add(name(bound)); - } - yield sj.toString(); - } - case LONG -> "long"; - case SHORT -> "short"; - case TYPEVAR -> name(((TypeVariable)t).asElement()); - default -> t.toString(); - }; - } - - static final String name(final Element e) { - return e instanceof QualifiedNameable qn ? name(qn) : name(e.getSimpleName()); - } - - private static final String name(final QualifiedNameable qn) { - final CharSequence n = qn.getQualifiedName(); - return n == null || n.isEmpty() ? name(qn.getSimpleName()) : name(n); - } - - private static final String name(final CharSequence cs) { - return cs instanceof String s ? s : cs.toString(); - } - - private static final boolean isInterface(final TypeMirror t) { - return t.getKind() == TypeKind.DECLARED && isInterface(((DeclaredType)t).asElement()); - } - - private static final boolean isInterface(final Element e) { - return e.getKind().isInterface(); - } - - private static final boolean returnTrue(final T ignored) { - return true; - } - - - /* - * Inner and nested classes. - */ - - - private final class SpecializationComparator implements Comparator { - - private SpecializationComparator() { - super(); - } - - @Override - public final int compare(final TypeMirror t, final TypeMirror s) { - if (t == s) { - return 0; - } else if (t == null) { - return 1; // nulls right - } else if (s == null) { - return -1; // nulls right - } else if (tes.sameType(t, s)) { - return 0; - } else if (tes.subtype(t, s)) { - // t is a subtype of s; s is a proper supertype of t - return -1; - } else if (tes.subtype(s, t)) { - // s is a subtype of t; t is a proper supertype of s - return 1; - } else { - return name(t).compareTo(name(s)); - } - } - - } - } diff --git a/src/main/java/org/microbean/bean/ConstantDescs.java b/src/main/java/org/microbean/bean/ConstantDescs.java index 937a046..7989c51 100644 --- a/src/main/java/org/microbean/bean/ConstantDescs.java +++ b/src/main/java/org/microbean/bean/ConstantDescs.java @@ -23,12 +23,12 @@ public final class ConstantDescs { public static final ClassDesc CD_Bean = ClassDesc.of("org.microbean.bean.Bean"); + public static final ClassDesc CD_BeanTypeMatcher = ClassDesc.of("org.microbean.bean.BeanTypeMatcher"); + public static final ClassDesc CD_Factory = ClassDesc.of("org.microbean.bean.Factory"); public static final ClassDesc CD_Id = ClassDesc.of("org.microbean.bean.Id"); - public static final ClassDesc CD_TypeMatcher = ClassDesc.of("org.microbean.bean.TypeMatcher"); - private ConstantDescs() { super(); } diff --git a/src/main/java/org/microbean/bean/Creation.java b/src/main/java/org/microbean/bean/Creation.java index da1a632..8eb5be2 100644 --- a/src/main/java/org/microbean/bean/Creation.java +++ b/src/main/java/org/microbean/bean/Creation.java @@ -1,6 +1,6 @@ /* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- * - * Copyright © 2023–2024 microBean™. + * Copyright © 2023–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 @@ -39,9 +39,9 @@ public interface Creation { * * @exception IllegalArgumentException if {@code instance} was found to be unsuitable for any reason * - * @idempotency Implementations of this method must be idempotent. + * @microbean.idempotency Implementations of this method must be idempotent. * - * @threadsafety Implementations of this method must be safe for concurrent use by multiple threads. + * @microbean.threadsafety Implementations of this method must be safe for concurrent use by multiple threads. */ // MUST be idempotent // For incomplete instances; see also https://stackoverflow.com/questions/50202523/creationalcontext-should-a-custom-bean-always-call-push-from-its-create-met diff --git a/src/main/java/org/microbean/bean/DefaultAutoCloseableRegistry.java b/src/main/java/org/microbean/bean/DefaultAutoCloseableRegistry.java index eece2ec..1dc39c0 100644 --- a/src/main/java/org/microbean/bean/DefaultAutoCloseableRegistry.java +++ b/src/main/java/org/microbean/bean/DefaultAutoCloseableRegistry.java @@ -1,6 +1,6 @@ /* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- * - * Copyright © 2023–2024 microBean™. + * Copyright © 2023–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 @@ -69,11 +69,11 @@ public DefaultAutoCloseableRegistry() { * * @exception IllegalStateException if this {@link DefaultAutoCloseableRegistry} is {@linkplain #closed() closed} * - * @nullability This method does not, and its overrides must not, return {@code null}. + * @microbean.nullability This method does not, and its overrides must not, return {@code null}. * - * @idempotency Overrides of this method must return new, distinct {@link DefaultAutoCloseableRegistry} instances. + * @microbean.idempotency Overrides of this method must return new, distinct {@link DefaultAutoCloseableRegistry} instances. * - * @threadsafety This method is, and its overrides must be, safe for concurrent use by multiple threads. + * @microbean.threadsafety This method is, and its overrides must be, safe for concurrent use by multiple threads. * * @see #register(AutoCloseable) */ @@ -102,9 +102,9 @@ public DefaultAutoCloseableRegistry newChild() { *

After any successful invocation of this method, an invocation of the {@link #closed()} method will forever after * return {@code true}.

* - * @idempotency This method is, and its overrides must be, idempotent. + * @microbean.idempotency This method is, and its overrides must be, idempotent. * - * @threadsafety This method is, and its overrides must be, safe for concurrent use by multiple threads. + * @microbean.threadsafety This method is, and its overrides must be, safe for concurrent use by multiple threads. * * @see #closed() */ @@ -167,9 +167,9 @@ public void close() { * * @return {@code true} if and only if this {@link DefaultAutoCloseableRegistry} is {@linkplain #close() closed} * - * @idempotency This method is idempotent. + * @microbean.idempotency This method is idempotent. * - * @threadsafety This method is safe for concurrent use by multiple threads. + * @microbean.threadsafety This method is safe for concurrent use by multiple threads. * * @see #close() */ @@ -198,9 +198,9 @@ public final boolean closed() { * * @exception NullPointerException if {@code closeable} is {@code null} * - * @idempotency This method is idempotent. + * @microbean.idempotency This method is idempotent. * - * @threadsafety This method is safe for concurrent use by multiple threads. + * @microbean.threadsafety This method is safe for concurrent use by multiple threads. */ @Override // AutoCloseableRegistry public final boolean register(final AutoCloseable closeable) { diff --git a/src/main/java/org/microbean/bean/DisposableReference.java b/src/main/java/org/microbean/bean/DisposableReference.java index 661a3ae..8deb12d 100644 --- a/src/main/java/org/microbean/bean/DisposableReference.java +++ b/src/main/java/org/microbean/bean/DisposableReference.java @@ -1,6 +1,6 @@ /* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- * - * Copyright © 2023–2024 microBean™. + * Copyright © 2023–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 @@ -182,9 +182,9 @@ public final boolean closed() { * @exception RuntimeException if an invocation of the {@link Consumer#accept(Object) accept(Object)} method on the * {@link Consumer} {@linkplain #DisposableReference(Object, Consumer) supplied at construction time} fails * - * @idempotency This method is idempotent. + * @microbean.idempotency This method is idempotent. * - * @threadsafety This method is safe for concurrent use by multiple threads. + * @microbean.threadsafety This method is safe for concurrent use by multiple threads. * * @see #DisposableReference(Object, Consumer) */ diff --git a/src/main/java/org/microbean/bean/QualifiersMatcher.java b/src/main/java/org/microbean/bean/EventQualifiersMatcher.java similarity index 52% rename from src/main/java/org/microbean/bean/QualifiersMatcher.java rename to src/main/java/org/microbean/bean/EventQualifiersMatcher.java index 019b5d8..44afff5 100644 --- a/src/main/java/org/microbean/bean/QualifiersMatcher.java +++ b/src/main/java/org/microbean/bean/EventQualifiersMatcher.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 @@ -15,8 +15,6 @@ import java.util.Collection; -import java.util.function.Predicate; - import org.microbean.qualifier.NamedAttributeMap; import static org.microbean.bean.Qualifiers.anyQualifier; @@ -24,39 +22,19 @@ import static org.microbean.bean.Qualifiers.defaultQualifiers; import static org.microbean.bean.Qualifiers.qualifiers; -public final class QualifiersMatcher implements Matcher>, Collection>> { +public final class EventQualifiersMatcher implements Matcher>, Collection>> { - public QualifiersMatcher() { + public EventQualifiersMatcher() { super(); } @Override // Matcher>, Collection>> public final boolean test(final Collection> receiverAttributes, final Collection> payloadAttributes) { + // "An event is delivered to an observer method if...the observer method has no event qualifiers or has a subset of + // the event qualifiers." final Collection> receiverQualifiers = qualifiers(receiverAttributes); - final Collection> payloadQualifiers = qualifiers(payloadAttributes); - if (receiverQualifiers.isEmpty()) { - // Pretend receiver had [@Default] and payload had at least [@Default] (e.g. [@Default, @Any]) - return payloadQualifiers.isEmpty() || containsAllMatching(payloadQualifiers::contains, defaultQualifiers()); - } else if (payloadQualifiers.isEmpty()) { - for (final NamedAttributeMap receiverQualifier : receiverQualifiers) { - if (anyQualifier(receiverQualifier) || defaultQualifier(receiverQualifier)) { - // receiver had [@Default] or [@Any] or [@Default, @Any]; pretend payload had [@Default, @Any]. - return true; - } - } - return false; - } - return containsAllMatching(payloadQualifiers::contains, receiverQualifiers); - } - - private static final boolean containsAllMatching(final Predicate p, final Iterable i) { - for (final Object o : i) { - if (!p.test(o)) { - return false; - } - } - return true; + return receiverQualifiers.isEmpty() || qualifiers(payloadAttributes).containsAll(receiverQualifiers); } } diff --git a/src/main/java/org/microbean/bean/EventTypeMatcher.java b/src/main/java/org/microbean/bean/EventTypeMatcher.java new file mode 100644 index 0000000..3a30354 --- /dev/null +++ b/src/main/java/org/microbean/bean/EventTypeMatcher.java @@ -0,0 +1,231 @@ +/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- + * + * 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 + * + * 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.bean; + +import java.util.List; +import java.util.Objects; + +import java.util.function.Predicate; + +import javax.lang.model.type.ArrayType; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.ReferenceType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.TypeVariable; +import javax.lang.model.type.WildcardType; + +import org.microbean.construct.Domain; + +// Experimental and basically located in the wrong package and module. +public final class EventTypeMatcher extends AbstractTypeMatcher implements Matcher { + + public EventTypeMatcher(final Domain domain) { + super(domain); + } + + @Override // Matcher + public final boolean test(final TypeMirror receiver, final TypeMirror payload) { + // https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#observer_resolution + return switch (receiver.getKind()) { + // Interestingly array types are never discussed explicitly in the specification's sections on observer + // resolution, but clearly they must be possible. + case ARRAY -> switch (payload.getKind()) { + case ARRAY -> this.identical(elementType(receiver), elementType(payload)); // never spelled out in the spec but inferred + case DECLARED -> false; + default -> throw illegalPayload(payload); + }; + // There is no mention in the specification of primitive and wrapper type interchangeability but it looks like + // that is where the specification is headed, and all existing implementations behave as if it were fully + // specified (see https://github.com/jakartaee/cdi/issues/829). + case BOOLEAN -> switch (payload.getKind()) { + case ARRAY -> false; + case DECLARED -> named((DeclaredType)payload, "java.lang.Boolean"); + default -> throw illegalPayload(payload); + }; + case BYTE -> switch (payload.getKind()) { + case ARRAY -> false; + case DECLARED -> named((DeclaredType)payload, "java.lang.Byte"); + default -> throw illegalPayload(payload); + }; + case CHAR -> switch (payload.getKind()) { + case ARRAY -> false; + case DECLARED -> named((DeclaredType)payload, "java.lang.Character"); + default -> throw illegalPayload(payload); + }; + case DOUBLE -> switch (payload.getKind()) { + case ARRAY -> false; + case DECLARED -> named((DeclaredType)payload, "java.lang.Double"); + default -> throw illegalPayload(payload); + }; + case FLOAT -> switch (payload.getKind()) { + case ARRAY -> false; + case DECLARED -> named((DeclaredType)payload, "java.lang.Float"); + default -> throw illegalPayload(payload); + }; + case INT -> switch (payload.getKind()) { + case ARRAY -> false; + case DECLARED -> named((DeclaredType)payload, "java.lang.Integer"); + default -> throw illegalPayload(payload); + }; + case LONG -> switch (payload.getKind()) { + case ARRAY -> false; + case DECLARED -> named((DeclaredType)payload, "java.lang.Long"); + default -> throw illegalPayload(payload); + }; + case SHORT -> switch (payload.getKind()) { + case ARRAY -> false; + case DECLARED -> named((DeclaredType)payload, "java.lang.Short"); + default -> throw illegalPayload(payload); + }; + case DECLARED -> switch (payload.getKind()) { + case ARRAY -> false; // implied + case DECLARED -> + this.identical(receiver, payload) || // interestingly this is never spelled out in the spec + this.assignable((DeclaredType)receiver, (DeclaredType)payload); + default -> throw illegalPayload(payload); + }; + // "An event type [payload] is considered assignable to a type variable [receiver] if the event type [payload] is + // assignable to the [condensed] upper bound [of the type variable [receiver], which may be an intersection type], + // if any [in which case the upper bound will be java.lang.Object]." + case TYPEVAR -> switch (payload.getKind()) { + // TODO: I think this is fully handled by covariantlyAssignable(rta, pta). + // case ARRAY, DECLARED -> assignableToCondensedTypeVariableBounds((TypeVariable)receiver, payload); + case ARRAY, DECLARED -> this.covariantlyAssignable(receiver, payload); + default -> throw illegalPayload(payload); + }; + default -> throw illegalReceiver(receiver); + }; + } + + private final boolean assignable(final DeclaredType receiver, final DeclaredType payload) { + assert receiver.getKind() == TypeKind.DECLARED; + assert payload.getKind() == TypeKind.DECLARED; + return switch (payload) { + + case DeclaredType parameterizedPayload when parameterized(payload) -> switch (receiver) { + // "A parameterized event type [parameterizedPayload] is considered assignable to..." + + case DeclaredType nonGenericOrRawReceiver when !generic(receiver) || raw(receiver) -> + // "...a [non-generic class or] raw observed event type [nonGenericOrRawReceiver] if the [non-generic class + // or] raw types are identical [undefined]." + this.identical(this.nonGenericClassOrRawType(nonGenericOrRawReceiver), + this.nonGenericClassOrRawType(parameterizedPayload)); + + case DeclaredType parameterizedReceiver -> { + // "...a parameterized observed event type [parameterizedReceiver]..." + assert parameterized(receiver); + if (this.identical(this.rawType(parameterizedReceiver), + this.rawType(parameterizedPayload))) { + // "...if they have identical raw type[s] [really if their declarations/elements are 'identical']..." + + final List rtas = parameterizedReceiver.getTypeArguments(); + final List ptas = parameterizedPayload.getTypeArguments(); + assert rtas.size() == ptas.size(); + for (int i = 0; i < rtas.size(); i++) { + final TypeMirror rta = rtas.get(i); + final TypeMirror pta = ptas.get(i); + // "...and for each parameter [type argument pair]..." + + switch (rta.getKind()) { + + case ARRAY: // rta + case DECLARED: // rta + // "...the observed event type parameter [receiver type argument, rta] is an actual type [a non-type + // variable, non-wildcard reference type] with identical raw type to the event type parameter [and is a + // non-generic class (or has a raw type) identical to the payload type argument (pta)]..." + if (this.identical(this.nonGenericClassOrRawType(rta), + pta)) { + // "...and, if the type [?] is parameterized [?]..." + if (cdiParameterized(rta)) { // really just yieldsRawType(rta) + assert cdiParameterized(pta); // ...because otherwise their raw types would not have been "identical" + // "...the event type parameter [receiver type argument, rta] is assignable to the observed event + // type parameter according to [all of] these rules..." + if (this.test(rta, pta)) { // note recursion + continue; + } + } else { + continue; // yes, trust me; vetted + } + } + yield false; + + case TYPEVAR: // rta + // "...the observed event type parameter [receiver type argument, rta] is a type variable and the event + // type parameter [payload type argument, pta] is assignable to the upper bound, if any [type variables + // have multiple bounds], of the type variable [receiver type argument, rta]" + /* + if (assignableToCondensedTypeVariableBounds((TypeVariable)rta, (ReferenceType)pta)) { + // TODO: I think this is fully handled by covariantlyAssignable(rta, pta). + continue; + } + */ + if (this.covariantlyAssignable(rta, pta)) { + continue; + } + yield false; + + case WILDCARD: // rta + // "...the observed event type parameter [receiver type argument, rta] is a wildcard and the event type + // parameter [payload type argument, pta] is assignable to the upper bound, if any, of the wildcard and + // assignable from the lower bound, if any, of the wildcard..." + /* + if (assignableToCondensedExtendsBound((WildcardType)rta, (ReferenceType)pta) && + assignableFromCondensedSuperBound((ReferenceType)pta, (WildcardType)rta)) { + // TODO: is this simply the contains() relationship? So basically: does rta contain pta? + continue; + } + */ + if (this.contains(rta, pta)) { + continue; + } + yield false; + + default: // rta + throw new AssertionError("rta: " + rta); // type arguments in Java can't be anything else + } // end switch(rta.getKind()) + + } // end type argument loop + yield true; // we passed all the type argument assignability tests + + } // end this.identical() check + yield false; // the type arguments' non-generic classes or raw types were not identical + } + + }; // end switch(receiver); end parameterizedPayload case + + case DeclaredType nonGenericOrRawPayload -> switch (receiver) { + // "A [non-generic or] raw event type [nonGenericOrRawayload] is considered assignable..." + + case DeclaredType parameterizedReceiver when parameterized(receiver) -> { + // "...to a parameterized observed event type [parameterizedReceiver] if the[ir] [non-generic classes or] raw + // types are identical and all type parameters [type arguments] of the required type [observed event type, + // receiver] are either unbounded type variables or java.lang.Object." + yield + this.identical(this.nonGenericClassOrRawType(parameterizedReceiver), + nonGenericOrRawPayload) && + allTypeArgumentsAre(parameterizedReceiver.getTypeArguments(), + ((Predicate)this::unboundedTypeVariable).or(this.domain()::javaLangObject)); + } + + // [Otherwise the payload is not assignable to the receiver; identity checking should have already happened in + // test(), not here.] + case DeclaredType nonGenericOrRawReceiver -> false; // or this.identical(nonGenericOrRawReceiver, nonGenericOrRawPayload); + + }; // end switch(receiver); end nonGenericOrRawPayload case + + }; // end switch(payload) + } + +} diff --git a/src/main/java/org/microbean/bean/EventTypes.java b/src/main/java/org/microbean/bean/EventTypes.java new file mode 100644 index 0000000..400898b --- /dev/null +++ b/src/main/java/org/microbean/bean/EventTypes.java @@ -0,0 +1,159 @@ +/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- + * + * 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 + * + * 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.bean; + +import java.lang.System.Logger; + +import java.util.List; +import java.util.Set; + +import javax.lang.model.element.Modifier; +import javax.lang.model.element.TypeElement; + +import javax.lang.model.type.ArrayType; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.TypeVariable; + +import org.microbean.construct.Domain; + +import org.microbean.construct.element.UniversalElement; + +import org.microbean.construct.type.UniversalType; + +import static java.lang.System.Logger.Level.WARNING; + +// Experimental and basically located in the wrong package and module. +public final class EventTypes extends Types { + + + /* + * Static fields. + */ + + + private static final Logger LOGGER = System.getLogger(EventTypes.class.getName()); + + + /* + * Constructors. + */ + + + public EventTypes(final Domain domain) { + super(domain); + } + + + /* + * Instance methods. + */ + + + public final List eventTypes(final TypeMirror t) { + final UniversalType ut = UniversalType.of(t, this.domain()); + // https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#event_types_and_qualifier_types + if (ut.getKind() == TypeKind.DECLARED) { + final UniversalElement e = ut.asElement(); + if (e.getKind().isInterface() || !e.getKind().isClass() || e.getModifiers().contains(Modifier.ABSTRACT)) { + // "An event object is an instance of a concrete Java class...." + if (LOGGER.isLoggable(WARNING)) { + LOGGER.log(WARNING, ut + " is an illegal event type"); + } + return List.of(); + } + } + // "The event types of the event include all superclasses and interfaces of the [concrete] runtime class of the + // event object." + return this.supertypes(ut, this::legalEventType); + } + + public final boolean legalEventType(final TypeMirror t) { + return legalEventType(UniversalType.of(t, this.domain())); + } + + public final boolean legalObservedEventType(final TypeMirror t) { + return legalObservedEventType(this.domain(), t); + } + + + /* + * Static methods. + */ + + + public static final boolean legalEventType(final Domain domain, final TypeMirror t) { + return legalEventType(UniversalType.of(t, domain)); + } + + static final boolean legalEventType(final UniversalType ut) { + // https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#event_types_and_qualifier_types + return switch (ut.getKind()) { + case ARRAY -> { + // Recurse into the component type. + if (!legalEventType(ut.getComponentType())) { // note recursion + if (LOGGER.isLoggable(WARNING)) { + LOGGER.log(WARNING, ut + " has a component type that is an illegal event type (" + ut.getComponentType()); + } + yield false; + } + yield true; + } + + // You can't fire a primitive event as of this writing, but there's nothing stopping a primitive event type from + // being legal otherwise. + // case BOOLEAN, BYTE, CHAR, DOUBLE, FLOAT, INT, LONG, SHORT -> true; + + case DECLARED -> { + // "An event type may not contain an unresolvable type variable. A wildcard type is not considered an unresolvable + // type variable." + // + // We interpret "contain" to mean "have as a type argument, recursively, anywhere". + for (final UniversalType uta : ut.getTypeArguments()) { + if (uta.getKind() != TypeKind.WILDCARD && !legalEventType(uta)) { // note recursion + if (LOGGER.isLoggable(WARNING)) { + LOGGER.log(WARNING, ut + " has a type argument that is an illegal event type (" + uta + ")"); + } + yield false; + } + } + yield true; + } + + default -> { + if (LOGGER.isLoggable(WARNING)) { + LOGGER.log(WARNING, ut + " is an illegal event type"); + } + yield false; + } + }; + + } + + public static final boolean legalObservedEventType(final Domain domain, final TypeMirror t) { + return legalObservedEventType(UniversalType.of(t, domain)); + } + + static final boolean legalObservedEventType(final UniversalType ut) { + // https://jakarta.ee/specifications/cdi/4.0/jakarta-cdi-spec-4.0#event_types_and_qualifier_types + // "Any Java type [that a method parameter element may bear] may be an observed event type." + return switch (ut.getKind()) { + case ARRAY, BOOLEAN, BYTE, CHAR, DECLARED, DOUBLE, FLOAT, INT, LONG, SHORT, TYPEVAR -> true; + default -> false; + }; + } + + +} diff --git a/src/main/java/org/microbean/bean/Factory.java b/src/main/java/org/microbean/bean/Factory.java index b7a614d..8a906f7 100644 --- a/src/main/java/org/microbean/bean/Factory.java +++ b/src/main/java/org/microbean/bean/Factory.java @@ -1,6 +1,6 @@ /* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- * - * Copyright © 2023–2024 microBean™. + * Copyright © 2023–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 @@ -92,8 +92,6 @@ public default boolean destroys() { // MUST be idempotent // If i is an AutoCloseable, MUST be idempotent - // - // TODO: rename back to destroy() public default void destroy(final I i, final Request creationRequest) { if (i instanceof AutoCloseable ac) { try { @@ -120,9 +118,9 @@ public default void destroy(final I i, final Request creationRequest) { * @return an {@link Optional} containing the nominal descriptor for this instance, if one can be constructed, or an * {@linkplain Optional#isEmpty() empty Optional} if one cannot be constructed * - * @threadsafety This method is safe for concurrent use by multiple threads. + * @microbean.threadsafety This method is safe for concurrent use by multiple threads. * - * @idempotency This method is neither idempotent nor deterministic. + * @microbean.idempotency This method is neither idempotent nor deterministic. */ @Override // Constable public default Optional describeConstable() { diff --git a/src/main/java/org/microbean/bean/Id.java b/src/main/java/org/microbean/bean/Id.java index c9b8a92..ec8c2e9 100644 --- a/src/main/java/org/microbean/bean/Id.java +++ b/src/main/java/org/microbean/bean/Id.java @@ -1,6 +1,6 @@ /* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- * - * Copyright © 2023–2024 microBean™. + * Copyright © 2023–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,7 +13,9 @@ */ package org.microbean.bean; +import java.lang.constant.ClassDesc; import java.lang.constant.Constable; +import java.lang.constant.ConstantDesc; import java.lang.constant.DynamicConstantDesc; import java.lang.constant.MethodHandleDesc; @@ -27,6 +29,8 @@ import org.microbean.constant.Constables; +import org.microbean.construct.Domain; + import org.microbean.qualifier.NamedAttributeMap; import org.microbean.scope.ScopeMember; @@ -43,24 +47,29 @@ import static org.microbean.qualifier.ConstantDescs.CD_NamedAttributeMap; -public final record Id(List types, +public final record Id(Domain domain, + List types, List> attributes, NamedAttributeMap governingScopeId, boolean alternate, int rank) implements Constable, Ranked, ScopeMember { - public Id(final List types, + private static final ClassDesc CD_Domain = ClassDesc.of(Domain.class.getName()); + + public Id(final Domain domain, + final List types, final List> attributes, final NamedAttributeMap governingScopeId) { - this(types, attributes, governingScopeId, false, Ranked.DEFAULT_RANK); + this(domain, types, attributes, governingScopeId, false, Ranked.DEFAULT_RANK); } - public Id(final List types, + public Id(final Domain domain, + final List types, final List> attributes, final NamedAttributeMap governingScopeId, final int rank) { - this(types, attributes, governingScopeId, false, rank); + this(domain, types, attributes, governingScopeId, false, rank); } public Id { @@ -71,7 +80,7 @@ public Id(final List types, } int i = 0; for (; i < size; i++) { - if (!legalBeanType(types.get(i))) { + if (!legalBeanType(domain, types.get(i))) { break; } } @@ -85,7 +94,7 @@ public Id(final List types, ++i; // skip past the illegal type i was pointing to for (; i < size; i++) { final TypeMirror t = types.get(i); - if (legalBeanType(t)) { + if (legalBeanType(domain, t)) { newTypes.add(t); } } @@ -101,21 +110,23 @@ public Id(final List types, @Override // Constable public final Optional> describeConstable() { - return Constables.describeConstable(this.attributes()) - .flatMap(attributesDesc -> this.governingScopeId().describeConstable() - .flatMap(governingScopeIdDesc -> Constables.describeConstable(this.types()) - .map(typesDesc -> DynamicConstantDesc.of(BSM_INVOKE, - MethodHandleDesc.ofConstructor(CD_Id, - CD_List, - CD_List, - CD_NamedAttributeMap, - CD_boolean, - CD_int), - typesDesc, - attributesDesc, - governingScopeIdDesc, - this.alternate() ? TRUE : FALSE, - this.rank())))); + return (this.domain() instanceof Constable c ? c.describeConstable() : Optional.empty()) + .flatMap(domainDesc -> Constables.describeConstable(this.attributes()) + .flatMap(attributesDesc -> this.governingScopeId().describeConstable() + .flatMap(governingScopeIdDesc -> Constables.describeConstable(this.types()) + .map(typesDesc -> DynamicConstantDesc.of(BSM_INVOKE, + MethodHandleDesc.ofConstructor(CD_Id, + CD_Domain, + CD_List, + CD_List, + CD_NamedAttributeMap, + CD_boolean, + CD_int), + typesDesc, + attributesDesc, + governingScopeIdDesc, + this.alternate() ? TRUE : FALSE, + this.rank()))))); } } diff --git a/src/main/java/org/microbean/bean/IdMatcher.java b/src/main/java/org/microbean/bean/IdMatcher.java index 1efa36b..459dd42 100644 --- a/src/main/java/org/microbean/bean/IdMatcher.java +++ b/src/main/java/org/microbean/bean/IdMatcher.java @@ -22,15 +22,15 @@ public final class IdMatcher implements Matcher { - private final QualifiersMatcher qm; + private final BeanQualifiersMatcher qm; private final InterceptorBindingsMatcher ibm; - private final TypeMatcher tm; + private final BeanTypeMatcher tm; - public IdMatcher(final QualifiersMatcher qm, + public IdMatcher(final BeanQualifiersMatcher qm, final InterceptorBindingsMatcher ibm, - final TypeMatcher tm) { + final BeanTypeMatcher tm) { super(); this.qm = Objects.requireNonNull(qm, "qm"); this.ibm = Objects.requireNonNull(ibm, "ibm"); diff --git a/src/main/java/org/microbean/bean/Initializer.java b/src/main/java/org/microbean/bean/Initializer.java index 56fb7b4..9af33ee 100644 --- a/src/main/java/org/microbean/bean/Initializer.java +++ b/src/main/java/org/microbean/bean/Initializer.java @@ -14,7 +14,7 @@ package org.microbean.bean; // Subordinate to Factory (really to PostInitializer). Normally applied to Producer output. -// Calls initializer methods. +// Calls initializer methods and injects fields // Note that this deliberately extends Aggregate, providing access to dependencies. public interface Initializer extends Aggregate { diff --git a/src/main/java/org/microbean/bean/Producer.java b/src/main/java/org/microbean/bean/Producer.java index a1b3863..07983e0 100644 --- a/src/main/java/org/microbean/bean/Producer.java +++ b/src/main/java/org/microbean/bean/Producer.java @@ -3,12 +3,12 @@ * Copyright © 2023–2024 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 + * 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 + * 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.bean; @@ -18,7 +18,12 @@ // Subordinate to Factory (really to Initializer) // Akin to CDI's Producer. // Handles instance production and disposal, *including intercepted production*. -// Does NOT handle initialization; see for example https://github.com/search?q=repo%3Aweld%2Fcore+%22.produce%28%29%22+language%3AJava&type=code +// +// Does NOT handle initialization; see for example +// https://github.com/search?q=repo%3Aweld%2Fcore+%22.produce%28%29%22+language%3AJava&type=code. Obviously it may +// acquire dependencies and supply them during production, but the point is it doesn't do field injection or initializer +// method invocation. +// // Does NOT handle post-initialization. // Does NOT handle business method interception. // Does NOT handle pre-disposal. diff --git a/src/main/java/org/microbean/bean/Ranked.java b/src/main/java/org/microbean/bean/Ranked.java index d42ce9e..3b61fcc 100644 --- a/src/main/java/org/microbean/bean/Ranked.java +++ b/src/main/java/org/microbean/bean/Ranked.java @@ -1,6 +1,6 @@ /* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- * - * Copyright © 2023–2024 microBean™. + * Copyright © 2023–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 @@ -157,9 +157,9 @@ public static boolean outranks(final Ranked r0, final Ranked r1) { * * @return {@code true} if and only if {@code i} outranks {@code j} * - * @idempotency This method is idempotent and deterministic. + * @microbean.idempotency This method is idempotent and deterministic. * - * @threadsafety This method is safe for concurrent use by multiple threads. + * @microbean.threadsafety This method is safe for concurrent use by multiple threads. */ public static boolean outranks(final int i, final int j) { return i > j; diff --git a/src/main/java/org/microbean/bean/RankedReducer.java b/src/main/java/org/microbean/bean/RankedReducer.java index 9186385..337590c 100644 --- a/src/main/java/org/microbean/bean/RankedReducer.java +++ b/src/main/java/org/microbean/bean/RankedReducer.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 @@ -157,11 +157,11 @@ public final T reduce(final List elements, * * @return a {@link RankedReducer} implementation; never {@code null} * - * @nullability This method never returns {@code null}. + * @microbean.nullability This method never returns {@code null}. * - * @idempotency This method is idempotent and deterministic. + * @microbean.idempotency This method is idempotent and deterministic. * - * @threadsafety This method is safe for concurrent use by multiple threads. + * @microbean.threadsafety This method is safe for concurrent use by multiple threads. */ @SuppressWarnings("unchecked") public static final Reducer of() { diff --git a/src/main/java/org/microbean/bean/Request.java b/src/main/java/org/microbean/bean/Request.java index d958634..a5dfa11 100644 --- a/src/main/java/org/microbean/bean/Request.java +++ b/src/main/java/org/microbean/bean/Request.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 @@ -33,11 +33,11 @@ public interface Request extends Creation, ReferenceSelector { * * @return the {@link BeanReduction} describing this {@link Request} in progress; never {@code null} * - * @idempotency Implementations of this method must be idempotent and deterministic. + * @microbean.idempotency Implementations of this method must be idempotent and deterministic. * - * @nullability Implementations of this method must not return {@code null}. + * @microbean.nullability Implementations of this method must not return {@code null}. * - * @threadsafety Implementations of this method must be safe for concurrent use by multiple threads. + * @microbean.threadsafety Implementations of this method must be safe for concurrent use by multiple threads. * * @see BeanReduction */ diff --git a/src/main/java/org/microbean/bean/TypeMatcher.java b/src/main/java/org/microbean/bean/TypeMatcher.java deleted file mode 100644 index 361bac0..0000000 --- a/src/main/java/org/microbean/bean/TypeMatcher.java +++ /dev/null @@ -1,900 +0,0 @@ -/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- - * - * Copyright © 2023–2024 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.bean; - -import java.lang.System.Logger; - -import java.lang.constant.ClassDesc; -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.Collections; -import java.util.List; -import java.util.Objects; -import java.util.Optional; - -import java.util.function.Predicate; - -import javax.lang.model.element.Element; -import javax.lang.model.element.ElementKind; -import javax.lang.model.element.Parameterizable; -import javax.lang.model.element.QualifiedNameable; - -import javax.lang.model.type.ArrayType; -import javax.lang.model.type.DeclaredType; -import javax.lang.model.type.IntersectionType; -import javax.lang.model.type.ReferenceType; -import javax.lang.model.type.TypeKind; -import javax.lang.model.type.TypeMirror; -import javax.lang.model.type.TypeVariable; -import javax.lang.model.type.WildcardType; - -import org.microbean.lang.TypeAndElementSource; - -import static java.lang.constant.ConstantDescs.BSM_INVOKE; - -import static org.microbean.lang.ConstantDescs.CD_TypeAndElementSource; - -/** - * A {@link Matcher} encapsulating CDI-compatible - * type matching rules. - * - * @author Laird Nelson - * - * @see #test(TypeMirror, TypeMirror) - */ -public final class TypeMatcher implements Constable, Matcher { - - - /* - * Static fields. - */ - - - private static final Logger LOGGER = System.getLogger(TypeMatcher.class.getName()); - - - /* - * Instance fields. - */ - - - private final TypeAndElementSource tes; - - - /* - * Constructors. - */ - - - /** - * Creates a new {@link TypeMatcher}. - * - * @param tes a {@link TypeAndElementSource}; must not be {@code null} - * - * @exception NullPointerException if {@code tes} is {@code null} - * - * @see TypeAndElementSource - */ - public TypeMatcher(final TypeAndElementSource tes) { - super(); - this.tes = Objects.requireNonNull(tes, "tes"); - } - - - /* - * Instance methods. - */ - - - /* - * Public methods. - */ - - /** - * Returns an {@link Optional} housing a {@link ConstantDesc} that represents this {@link TypeMatcher}. - * - *

This method never returns {@code null}.

- * - *

The default implementation of this method relies on the presence of a {@code public} constructor that accepts a - * single {@link TypeAndElementSource}-typed argument.

- * - *

The {@link Optional} returned by an invocation of this method may be, and often will be, {@linkplain - * Optional#isEmpty() empty}.

- * - * @return an {@link Optional} housing a {@link ConstantDesc} that represents this {@link TypeMatcher}; never {@code - * null} - * - * @see Constable#describeConstable() - */ - @Override // Constable - public final Optional describeConstable() { - return (this.tes instanceof Constable c ? c.describeConstable() : Optional.empty()) - .map(tesDesc -> DynamicConstantDesc.of(BSM_INVOKE, - MethodHandleDesc.ofConstructor(ClassDesc.of(this.getClass().getName()), - CD_TypeAndElementSource), - tesDesc)); - } - - /** - * Returns {@code true} if and only if the supplied {@code payload} argument matches - * the supplied {@code receiver} argument, according to the rules defined by section - * 2.4.2.1 of the CDI specification. - * - * @param receiver the left hand side of a type assignment; must not be {@code null} - * - * @param payload the right hand side of a type assignment; must not be {@code null} - * - * @return {@code true} if and only if the supplied {@code payload} argument matches - * the supplied {@code receiver} argument, according to the rules defined by section - * 2.4.2.1 of the CDI specification; {@code false} otherwise - * - * @exception NullPointerException if either argument is {@code null} - * - * @exception IllegalArgumentException if either type is any type other than an {@linkplain TypeKind#ARRAY array - * type}, a {@link TypeKind#isPrimitive() primitive type}, or a {@linkplain TypeKind#DECLARED declared type} - */ - // Is the payload assignable to the receiver? That is, does the payload "match the receiver", in CDI parlance? - @Override - public final boolean test(final TypeMirror receiver, final TypeMirror payload) { - // "A bean is assignable to a given injection point if: - // - // "The bean has a bean type [payload] that matches the required type [receiver]. For this purpose..." - return receiver == Objects.requireNonNull(payload, "payload") || switch (receiver.getKind()) { - // "...primitive types are considered to match their corresponding wrapper types in java.lang..." - case BOOLEAN -> payload.getKind() == TypeKind.BOOLEAN || declaredTypeNamed(payload, "java.lang.Boolean"); - case BYTE -> payload.getKind() == TypeKind.BYTE || declaredTypeNamed(payload, "java.lang.Byte"); - case CHAR -> payload.getKind() == TypeKind.CHAR || declaredTypeNamed(payload, "java.lang.Character"); - case DOUBLE -> payload.getKind() == TypeKind.DOUBLE || declaredTypeNamed(payload, "java.lang.Double"); - case FLOAT -> payload.getKind() == TypeKind.FLOAT || declaredTypeNamed(payload, "java.lang.Float"); - case INT -> payload.getKind() == TypeKind.INT || declaredTypeNamed(payload, "java.lang.Integer"); - case LONG -> payload.getKind() == TypeKind.LONG || declaredTypeNamed(payload, "java.lang.Long"); - case SHORT -> payload.getKind() == TypeKind.SHORT || declaredTypeNamed(payload, "java.lang.Short"); - // "...and array types are considered to match only if their element types [note: not component types] are - // identical ['identical' is actually undefined in the specification]..." - case ARRAY -> payload.getKind() == TypeKind.ARRAY && this.identical(elementType(receiver), elementType(payload)); - case DECLARED -> switch (payload.getKind()) { - // "...primitive types are considered to match their corresponding wrapper types in java.lang..." - case BOOLEAN -> named((DeclaredType)receiver, "java.lang.Boolean"); - case BYTE -> named((DeclaredType)receiver, "java.lang.Byte"); - case CHAR -> named((DeclaredType)receiver, "java.lang.Character"); - case DOUBLE -> named((DeclaredType)receiver, "java.lang.Double"); - case FLOAT -> named((DeclaredType)receiver, "java.lang.Float"); - case INT -> named((DeclaredType)receiver, "java.lang.Integer"); - case LONG -> named((DeclaredType)receiver, "java.lang.Long"); - case SHORT -> named((DeclaredType)receiver, "java.lang.Short"); - // "Parameterized and raw types [and non-generic classes, and non-array types]..." - case DECLARED -> - // "...are considered to match if they are identical [undefined]..." - this.identical(receiver, payload) || - // "...or if the bean type [payload] is assignable to [see #assignable(TypeMirror, TypeMirror)] the required type - // [receiver]...." - this.assignable((DeclaredType)receiver, (DeclaredType)payload); - default -> throw new IllegalArgumentException("Illegal payload kind: " + payload.getKind() + "; payload: " + payload); - }; - default -> throw new IllegalArgumentException("Illegal receiver kind: " + receiver.getKind() + "; receiver: " + receiver); - }; - } - - /** - * Returns the {@link TypeAndElementSource} used by this {@link TypeMatcher}. - * - * @return the {@link TypeAndElementSource} used by this {@link TypeMatcher}; never {@code null} - * - * @see #TypeMatcher(TypeAndElementSource) - * - * @see TypeAndElementSource - */ - public final TypeAndElementSource typeAndElementSource() { - return this.tes; - } - - /* - * Private methods. - */ - - private final boolean assignable(final DeclaredType receiver, final DeclaredType payload) { - assert receiver.getKind() == TypeKind.DECLARED; - assert payload.getKind() == TypeKind.DECLARED; - return switch (payload) { - // "A parameterized bean type [payload] is considered assignable..." - case DeclaredType parameterizedPayload when parameterized(payload) -> switch (receiver) { - // "...to a [non-generic class or] raw required type [receiver]..." - case TypeMirror rawReceiver when !generic(receiver) || raw(receiver) -> { - // "...if the [non-generic class or] raw types are identical [undefined] and all type parameters [type - // arguments] of the bean type [payload] are either unbounded type variables [undefined] or java.lang.Object." - yield - this.identical(this.nonGenericClassOrRawType(rawReceiver), this.nonGenericClassOrRawType(parameterizedPayload)) && - allTypeArgumentsAre(parameterizedPayload.getTypeArguments(), - ((Predicate)this::unboundedTypeVariable).or(TypeMatcher::isJavaLangObject)); - } - // "...to a parameterized required type [receiver]..." - case DeclaredType parameterizedReceiver when parameterized(receiver) -> { - // "...if they have identical raw type [really if their declarations/elements are 'identical']..." - if (this.identical(this.rawType(parameterizedReceiver), this.rawType(parameterizedPayload))) { - // "...and for each parameter [type argument pair]..." - final List rtas = parameterizedReceiver.getTypeArguments(); - final List ptas = parameterizedPayload.getTypeArguments(); - assert rtas.size() == ptas.size(); - for (int i = 0; i < rtas.size(); i++) { - final TypeMirror rta = rtas.get(i); - final TypeMirror pta = ptas.get(i); - // "...the required [receiver] type parameter [type argument] and the bean [payload] type parameter [type - // argument] are actual types [non-type variable, non-wildcard reference types]..." - if (actual(rta)) { - if (actual(pta)) { - // "...with identical [non-generic classes or] raw type[s]..." - if (this.identical(this.nonGenericClassOrRawType(rta), this.nonGenericClassOrRawType(pta))) { - // "...and, if the type [?] is parameterized [?]..." - // - // Let rta and pta be array types with parameterized element types, such as List[] and - // List[]. Then their raw types are List[] and List[]. Their parameterized element types are - // List and List. According to the JLS, neither List[] nor List[] is - // parameterized. - // - // In this example, if we get here, we have only proven that List[] is "identical to" List[]. - // - // The "if the type is parameterized" clause is tough. Which type is under discussion? "the type" - // seems to refer to the "identical raw type". But a raw type by definition is not parameterized, so - // this clause would seem to be superfluous and thus never apply, and so List[] := - // List[] is OK. Oops. So not that interpretation. - // - // What if instead the "if the type is parameterized" clause means the receiver type itself and - // somehow loosely the payload type as well? Well, clearly it cannot correctly do this, since an - // array type is never a parameterized type. Same bad result. Oops. So not that interpretation. - // - // Or what if "identical raw type" really means "identical raw type (or identical component type if - // they are array types)"? That would be most efficient, since it would rule out List[] := - // List[] right off the bat: we wouldn't even get here. But that really doesn't seem to be - // what is meant. So probably not that interpretation. - // - // What if the "if the type is parameterized" clause really means "if the element type declaration - // used by both the receiver and payload types is generic"? That would work. That's also - // semantically equivalent to something like: "...if at least one of the two arguments is a - // parameterized type [e.g. List, not List[], not String, not List], or at least one - // of the two types is an array type with a parameterized element type [e.g. List[], not - // List[], not String[]]..." - // - // That is the interpretation we apply here. So: - // - // "...and, if the type [?] is parameterized [?]..." - // - // becomes: - // - // "...and, if at least one of the two type arguments is a parameterized type, or if at least one - // of the two types is an array type with a parameterized element type..." - // - // That, in turn, designates any type capable of properly yielding a raw type, while ruling out - // those that can't! That means it is exactly equal to our yieldsRawType(TypeMirror) method, and so - // that's what cdiParameterized(TypeMirror) returns (go see for yourself). - if (cdiParameterized(rta)) { // really just yieldsRawType(rta) - assert cdiParameterized(pta); // ...because otherwise their raw types would not have been "identical" - // "...the bean type parameter [type argument] is assignable to the required type parameter [type - // argument] according to [all of] these rules [including 'matching']..." - if (test(rta, pta)) { // note recursion - continue; - } - yield false; - } else { - assert !cdiParameterized(pta); - continue; // yes, trust me; vetted - } - } - yield false; - } else if (pta.getKind() == TypeKind.TYPEVAR) { - // "...the required [receiver] type parameter [type argument] is an actual type [a non-type variable, - // non-wildcard reference type], the bean [payload] type parameter [type argument] is a type variable and - // the actual type [non-type variable, non-wildcard reference type] [required type argument, receiver] is - // assignable to the upper bound, if any, of the type variable [bean type argument, payload] [type - // variables have multiple bounds]..." - // - // (This is weird. Yes, the *receiver* type argument is being tested to see if it is assignable to the - // *payload* type argument.) - if (assignableToCondensedTypeVariableBounds((TypeVariable)pta, (ReferenceType)rta)) { - continue; - } - yield false; - } - throw new AssertionError("Unexpected payload type argument kind: " + pta.getKind()); - } else if (rta.getKind() == TypeKind.WILDCARD) { - // "...the required type parameter [type argument] is a wildcard, the bean type parameter [type - // argument] is an actual type [a non-type variable, non-wildcard reference type]..." - if (actual(pta)) { - // "...and the actual type [non-type variable, non-wildcard reference type] is assignable to the upper - // bound, if any, of the wildcard and assignable from the lower bound, if any of the wildcard" - if (assignableToCondensedExtendsBound((WildcardType)rta, (ReferenceType)pta) && - assignableFromCondensedSuperBound((ReferenceType)pta, (WildcardType)rta)) { - continue; - } - yield false; - } else if (pta.getKind() == TypeKind.TYPEVAR) { - // "...the required [receiver] type parameter [type argument] is a wildcard, the bean [payload] type - // parameter [type argument] is a type variable and the upper bound of the type variable [a type - // variable has many bounds?] is assignable to or assignable from the upper bound, if any, of the - // wildcard and assignable from the lower bound, if any, of the wildcard" - if ((condensedTypeVariableBoundsAssignableToCondensedExtendsBound((WildcardType)rta, (TypeVariable)pta) || - condensedTypeVariableBoundsAssignableFromCondensedExtendsBound((TypeVariable)pta, (WildcardType)rta)) && - condensedTypeVariableBoundsAssignableFromCondensedSuperBound((TypeVariable)pta, (WildcardType)rta)) { - continue; - } - yield false; - } - throw new AssertionError("Unexpected payload type argument kind: " + pta.getKind()); - } else if (rta.getKind() == TypeKind.TYPEVAR) { - if (pta.getKind() == TypeKind.TYPEVAR) { - // "...the required [receiver] type parameter [type argument] and the bean [payload] type parameter - // [type argument] are both type variables and the upper bound of the required [receiver] type - // parameter [type argument] [a type variable has many bounds?] is assignable to the upper bound [a - // type variable has many bounds?], if any, of the bean [payload] type parameter [type argument]" - // - // (This is weird. Yes, the *receiver* type argument is being tested to see if it is assignable to the - // *payload* type argument.) - if (condensedTypeVariableBoundsAssignableToCondensedTypeVariableBounds((TypeVariable)pta, (TypeVariable)rta)) { - continue; - } - yield false; - } - throw new AssertionError("Unexpected payload type argument kind: " + pta.getKind()); - } - throw new AssertionError("Unexpected receiver type argument kind: " + rta.getKind()); - } - yield true; // we passed all the type argument assignability tests - } - yield false; // the type arguments' non-generic classes or raw types were not identical - } - default -> throw new AssertionError("Unexpected receiver kind: " + receiver.getKind()); - }; - // "A [non-generic or] raw bean type [payload] is considered assignable..." - case TypeMirror nonGenericOrRawPayload -> switch (receiver) { - // "...to a parameterized required type [receiver]..." - case DeclaredType parameterizedReceiver when parameterized(receiver) -> { - // "...if the[ir] [non-generic classes or] raw types are identical and all type parameters [type arguments] of - // the required type [receiver] are either unbounded type variables or java.lang.Object." - yield - this.identical(this.nonGenericClassOrRawType(parameterizedReceiver), nonGenericOrRawPayload) && - allTypeArgumentsAre(parameterizedReceiver.getTypeArguments(), - ((Predicate)this::unboundedTypeVariable).or(TypeMatcher::isJavaLangObject)); - } - // [Otherwise the payload is not assignable to the receiver; identity checking should have already happened in - // matches(), not here.] - case DeclaredType nonGenericOrRawReceiver when receiver.getKind() == TypeKind.DECLARED -> { - yield false; // or yield this.identical(nonGenericOrRawReceiver, nonGenericOrRawPayload); - } - default -> throw new AssertionError("Unexpected payload kind: " + payload.getKind() + "; receiver: " + receiver + "; payload: " + payload); - }; - }; - } - - // Is payload "identical to" receiver, following the intent of CDI? The relation "identical to" is not defined in the - // specification. Does it mean ==? Does it mean equals()? Does it mean - // javax.lang.model.util.Types#isSameType(TypeMirror, TypeMirror)? Something else? - // - // This implementation chooses TypeAndElementSource#sameType(TypeMirror, TypeMirror), but with one - // change. TypeAndElementSource#sameType(TypeMirror, TypeMirror) is usually backed by the - // javax.lang.model.util.Types#isSameType(TypeMirror, TypeMirror) method. That method will return false if either - // argument is a wildcard type. This method first checks to see if the arguments are the same Java references (==), - // regardless of type. - private final boolean identical(final TypeMirror receiver, final TypeMirror payload) { - // CDI has an undefined notion of "identical to". This method attempts to divine and implement the intent. Recall - // that javax.lang.model.* compares types with "sameType" semantics. - return receiver == payload || this.tes.sameType(receiver, payload); - } - - // Is the "actual" type argument represented by payload assignable to all of the receiver's condensed bounds? - // - // Pay close attention to how this is called, i.e. what is payload and what is receiver is often "backwards". For - // example: - // - // @Inject Foo foo; <-- Bean // is the String "actual type" (payload) assignable to T's (receiver's) CharSequence bound? - // // ^ payload! ^ receiver! - // - // Recall that after condensing "T extends CharSequence" you get CharSequence. - // - // Recall that if, instead, you had T extends S and S extends CharSequence, condensing T still yields CharSequence. - private final boolean assignableToCondensedTypeVariableBounds(final TypeVariable receiver, final ReferenceType payload) { - assert receiver.getKind() == TypeKind.TYPEVAR; - assert actual(payload); // We can assert this only because of where the method is called. - return covariantlyAssignable(List.of(receiver), List.of(payload)); // deliberately List.of(payload) and not condense(payload) - } - - // Is candidate covariantly assignable to w's condensed extends (upper) bound? - private final boolean assignableToCondensedExtendsBound(final WildcardType w, final ReferenceType candidate) { - assert w.getKind() == TypeKind.WILDCARD; - assert actual(candidate); // We can assert this only because of where this method is called from. - final ReferenceType extendsBound = (ReferenceType)w.getExtendsBound(); - return extendsBound == null || switch (extendsBound.getKind()) { - case ARRAY, DECLARED -> covariantlyAssignable(extendsBound, candidate); - case TYPEVAR -> covariantlyAssignable((ReferenceType)this.condense(extendsBound), List.of(candidate)); - default -> throw new AssertionError(); - }; - } - - // It's not immediately clear what CDI means by a type variable's upper bound. In javax.lang.model.type parlance, the - // upper bound of a TypeVariable could be an IntersectionType, which java.lang.reflect.TypeVariable represents as a - // collection of bounds. CDI blundered into this earlier: https://issues.redhat.com/browse/CDI-440 and - // https://github.com/jakartaee/cdi/issues/682 - // - // "the upper bound of the required type parameter [receiver type argument] is assignable to the upper bound, if any, - // of the bean type parameter [payload type argument]" (when both arguments are type variables) should *actually* - // read: - // - // "for each bound, PTA, of the bean type parameter [payload type argument], there is at least one bound, RTA, of the - // required type parameter [receiver type argument], which is assignable to PTA." - // - // The TCK enforces this, even though it's not in the specification (!). - // - // Weld's implementation confuses type parameters with arguments, just like the specification. They have a series of - // methods implemented as part of PR 614 (https://github.com/weld/core/pull/614) named "parametersMatch" [arguments - // match]. - // - // Weld also has methods named things like "getUppermostTypeVariableBounds" and "getUppermostBounds". These appear to - // "condense" useless type variable extensions to "get to" the "real" types involved. So T extends S extends String - // becomes String. - // - // Digging deeper, you (I) might think getUppermostTypeVariableBounds(tv) is just erase(tv) applied recursively. But I - // think you would be wrong. - // - // Start with: - // https://github.com/openjdk/jdk/blob/181845ae46157a9bb3bf8e2a328fa59eddc0273a/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java#L2450 - // - // Compare vs.: - // https://github.com/weld/core/blob/e894d1699ff1c91332605f5ecae5f53410effb81/impl/src/main/java/org/jboss/weld/resolution/AbstractAssignabilityRules.java#L57-L62 - // - // To illustrate the difference between the two operations, recursive erasure (according to the Java Language - // Specification) of the pseudo-declaration T extends S extends List & Serializable would yield, simply, List - // (T would erease to "the erasure of its leftmost bound", which would be the erasure of S, which would be "the - // erasure of its leftmost bound", which would be the erasure of List, which would be List. Serializable just - // gets dropped.) - // - // By contrast, Weld's getUppermostTypeVariableBounds(T) operation would yield [List, Serializable] (have not - // tested this with code, just reading). - // - // I think this is what the javac compiler calls, somewhat confusingly, "classBound": - // https://github.com/openjdk/jdk/blob/jdk-24%2B7/src/jdk.compiler/share/classes/com/sun/tools/javac/code/Types.java#L2760-L2796 - // - // So then: - // - // For every bound in (condensed) receiverBounds, is there a bound in (condensed) payloadBounds that is covariantly - // assignable to it? - // - // (Is there one bound in (condensed) payloadBounds that matches all bounds in (condensed) receiver bounds?) - // - // Throws ClassCastException if, after condensing, any encountered bound is not either an ArrayType or a DeclaredType. - private final boolean covariantlyAssignable(final List receiverBounds, List payloadBounds) { - payloadBounds = this.condense(payloadBounds); - for (final TypeMirror condensedReceiverBound : this.condense(receiverBounds)) { - if (!covariantlyAssignable((ReferenceType)condensedReceiverBound, payloadBounds)) { - return false; - } - } - return true; - } - - // Is there a DeclaredType-or-ArrayType bound in condensedPayloadBounds that is assignable to classOrArrayTypeReceiver - // using Java, not CDI, assignability semantics? - // - // It is assumed condensedPayloadBounds is the result of a condense() call. - // - // Throws ClassCastException or IllegalArgumentException if any encountered type is neither an ArrayType nor a - // DeclaredType. - private final boolean covariantlyAssignable(final ReferenceType classOrArrayTypeReceiver, final List condensedPayloadBounds) { - assert actual(classOrArrayTypeReceiver); // We can assert this only because of where this method is called. - return switch (classOrArrayTypeReceiver.getKind()) { - case ARRAY, DECLARED -> { - for (final TypeMirror condensedPayloadBound : condensedPayloadBounds) { - assert actual(condensedPayloadBound); - if (covariantlyAssignable(classOrArrayTypeReceiver, (ReferenceType)condensedPayloadBound)) { - yield true; - } - } - yield false; - } - default -> throw new IllegalArgumentException("classOrArrayTypeReceiver: " + classOrArrayTypeReceiver + "; kind: " + classOrArrayTypeReceiver.getKind()); - }; - } - - // Is classOrArrayTypePayload assignable to classOrArrayTypeReceiver following the rules of Java assignability - // (i.e. covariance)? - // - // The types are ReferenceTypes because this is only ever invoked in the context of type arguments. - private final boolean covariantlyAssignable(final ReferenceType classOrArrayTypeReceiver, final ReferenceType classOrArrayTypePayload) { - assert actual(classOrArrayTypeReceiver); // Based on where and how this method is called. - assert actual(classOrArrayTypePayload); // Based on where and how this method is called. - return - classOrArrayTypeReceiver == classOrArrayTypePayload || // Optimization - // Note that TypeAndElementSource#assignable(TypeMirror, TypeMirror) follows the lead of - // javax.lang.model.util.Types#isAssignable(TypeMirror, TypeMirror) where the "payload" is the *first* parameter - // and the "receiver" is the *second* argument. - this.tes.assignable(classOrArrayTypePayload, classOrArrayTypeReceiver); // yes, "backwards" - } - - // Are payload's condensed bounds assignable to receiver's condensed extends bound (upper bound)? - private final boolean condensedTypeVariableBoundsAssignableToCondensedExtendsBound(final WildcardType receiver, final TypeVariable payload) { - assert receiver.getKind() == TypeKind.WILDCARD; - assert payload.getKind() == TypeKind.TYPEVAR; - // "...the upper bound of the type variable [a type variable has many bounds?] is assignable TO [...] the upper - // bound, if any, of the wildcard..." - final TypeMirror extendsBound = receiver.getExtendsBound(); - // No need to condense arguments to eliminate useless type variables and intersection types so that Java covariant - // semantics will work properly in this case; #covariantlyAssignable(List, List) does this already. - return extendsBound == null || covariantlyAssignable(List.of((ReferenceType)extendsBound), List.of(payload)); - } - - // Is payload's condensed extends bound (upper bound) covariantly assignable to receiver's condensed bounds? - private final boolean condensedTypeVariableBoundsAssignableFromCondensedExtendsBound(final TypeVariable receiver, final WildcardType payload) { - assert receiver.getKind() == TypeKind.TYPEVAR; - assert payload.getKind() == TypeKind.WILDCARD; - // "...the upper bound of the type variable [a type variable has many bounds?] is assignable [...] FROM the upper - // bound, if any, of the wildcard..." - final TypeMirror extendsBound = payload.getExtendsBound(); - // No need to condense arguments to eliminate useless type variables and intersection types so that Java covariant - // semantics will work properly in this case; #covariantlyAssignable(List, List) does this already. - return extendsBound == null || covariantlyAssignable(List.of(receiver), List.of((ReferenceType)extendsBound)); - } - - // Is payload's super bound (lower bound) covariantly assignable to receiver's condensed bounds? - private final boolean condensedTypeVariableBoundsAssignableFromCondensedSuperBound(final TypeVariable receiver, final WildcardType payload) { - assert receiver.getKind() == TypeKind.TYPEVAR; - assert payload.getKind() == TypeKind.WILDCARD; - final TypeMirror superBound = payload.getSuperBound(); - // No need to condense arguments to eliminate useless type variables and intersection types so that Java covariant - // semantics will work properly in this case; #covariantlyAssignable(List, List) does this already. - return superBound == null || covariantlyAssignable(List.of(receiver), List.of(superBound)); - } - - // Are payload's condensed bounds covariantly assignable to receiver's condensed bounds? - private final boolean condensedTypeVariableBoundsAssignableToCondensedTypeVariableBounds(final TypeVariable receiver, final TypeVariable payload) { - assert receiver.getKind() == TypeKind.TYPEVAR; - assert payload.getKind() == TypeKind.TYPEVAR; - // No need to condense arguments to eliminate useless type variables and intersection types so that Java covariant - // semantics will work properly in this case; #covariantlyAssignable(List, List) does this already. - return covariantlyAssignable(List.of(receiver), List.of(payload)); - } - - // Is payload's condensed super bound (lower bound) covariantly assignable to receiver? - // - // Since either of a wildcard type's bounds may be a type variable, and since a type variable's bound may be an - // intersection type, it follows that after condensation either of a wildcard type's bounds may be a list of actual - // types. - // - // Therefore this is really: Is there one bound among payload's actual super bounds that is assignable to receiver? - private final boolean assignableFromCondensedSuperBound(final ReferenceType receiver, final WildcardType payload) { - assert payload.getKind() == TypeKind.WILDCARD; - assert actual(receiver); // We can assert this only because of where this method is called from. - final ReferenceType superBound = (ReferenceType)payload.getSuperBound(); - return superBound == null || switch (superBound.getKind()) { - case ARRAY, DECLARED -> covariantlyAssignable(receiver, superBound); - case TYPEVAR -> covariantlyAssignable(receiver, (ReferenceType)this.condense(superBound)); - default -> throw new AssertionError(); - }; - } - - // Get the raw type yielded by t, assuming t is the sort of type that can yield a raw type. - // - // An array type with a parameterized element type can yield a raw type. - // - // A declared type that is parameterized can yield a raw type. - // - // No other type yields a raw type. - // - // t cannot be null. - private final TypeMirror rawType(final TypeMirror t) { - return switch (t.getKind()) { - case ARRAY -> { - final TypeMirror et = elementType(t); - if (!parameterized(et)) { - throw new IllegalArgumentException("t is an array type whose element type is not parameterized so cannot yield a raw type"); - } - yield this.arrayTypeOf(this.erasure(et)); - } - case DECLARED -> { - if (!parameterized(t)) { - throw new IllegalArgumentException("t is a declared type that is not parameterized so cannot yield a raw type"); - } - yield this.erasure(t); - } - default -> throw new IllegalArgumentException("t is a " + t.getKind() + " type and so cannot yield a raw type"); - }; - } - - // Return t if its element declares a non-generic class, or if it is the raw type usage of a generic class. - private final TypeMirror nonGenericClassOrRawType(final TypeMirror t) { - return yieldsRawType(t) ? this.rawType(t) : t; - } - - // Erase t and return its erasure. Erasing is a complex business that can involve the creation of new types. - private final TypeMirror erasure(final TypeMirror t) { - return this.tes.erasure(t); - } - - // Return a possibly new ArrayType whose component type is the supplied componentType. - private final ArrayType arrayTypeOf(final TypeMirror componentType) { - return this.tes.arrayTypeOf(componentType); - } - - // "Condenses" t, according to the following rules. - // - // If t is null, returns List.of(). - // - // If t is a type variable, returns the result of condensing its upper bound (thus eliminating the type variable). - // - // If t is an intersection type, returns the result of condensing its bounds (thus eliminating the intersection type). - // - // If t is a wildcard type, returns the result of condensing either its super or extends bound (thus eliminating the - // wildcard type). The result of condensing a wildcard type that declares neither bound (?) is the result of - // condensing the type declared by java.lang.Object. - // - // In all other cases returns List.of(t). - // - // Note especially this is not type erasure, though it has some similarities. - // - // See #condense(List) below. - private final List condense(final TypeMirror t) { - return t == null ? List.of() : switch (t.getKind()) { - case INTERSECTION -> this.condense(((IntersectionType)t).getBounds()); - case TYPEVAR -> this.condense(((TypeVariable)t).getUpperBound()); - case WILDCARD -> { - final WildcardType w = (WildcardType)t; - final TypeMirror s = w.getSuperBound(); - final TypeMirror e = w.getExtendsBound(); - if (s == null) { - yield e == null ? List.of(this.tes.declaredType("java.lang.Object")) : this.condense(e); - } else if (e == null) { - yield this.condense(s); - } else { - throw new AssertionError(); - } - } - default -> List.of(t); // t's bounds are defined solely by itself, or it's a wildcard and we didn't say which bounds - }; - } - - // If ts is empty, returns an empty unmodifiable List. - // - // If ts consists of a single element, returns the result of condensing t (see #condense(TypeMirror) above). - // - // Otherwise creates a new list, condenses each element of ts, and adds each element of the condensation to the new - // list, and returns an unmodifiable view of the new list. - // - // Note that deliberately duplicates are not eliminated from the new list. - private final List condense(final List ts) { - final int size = ts.isEmpty() ? 0 : ts.size(); - if (size == 0) { - return List.of(); - } - // We take care not to allocate/copy new lists needlessly. - ArrayList newBounds = null; - for (int i = 0; i < size; i++) { - final TypeMirror t = ts.get(i); - switch (t.getKind()) { - case INTERSECTION: - case TYPEVAR: - case WILDCARD: - // These are the only types where condensing does anything. In these cases alone we need to make a new list. - if (newBounds == null) { - newBounds = new ArrayList<>(size * 2); - if (i > 0) { - // All ts up to this point have been non-condensable types, so catch up and add them as-is. - newBounds.addAll(ts.subList(0, i)); - } - } - newBounds.addAll(condense(t)); - break; - default: - if (newBounds != null) { - newBounds.add(t); - } - break; - } - } - if (newBounds == null) { - return ts; - } - newBounds.trimToSize(); - return Collections.unmodifiableList(newBounds); - } - - // Is t an unbounded type variable? - // - // CDI does not define what an "unbounded type variable" is. This method attempts to divine and implement the intent. - // - // Since according to the Java Language Specification, all type variables have an upper bound, which in the - // pathological case is java.lang.Object, it would seem that "unbounded" means "has java.lang.Object as its upper - // bound". - // - // It is not clear in the case of T extends S whether T is an unbounded TypeVariable or not. This interpretation - // behaves as if it is. - // - // Weld seems to take the position that an unbounded type variable is one that has java.lang.Object as its sole upper - // bound; see - // https://github.com/weld/core/blob/5.1.2.Final/impl/src/main/java/org/jboss/weld/util/Types.java#L258. Under this - // interpretation T extends S would not be considered an unbounded type variable. Type variable bounds are erased in - // every other situation in CDI. - private final boolean unboundedTypeVariable(final TypeMirror t) { - return switch (t.getKind()) { - case TYPEVAR -> { - final List condensedBounds = this.condense(((TypeVariable)t).getUpperBound()); - yield condensedBounds.size() == 1 && isJavaLangObject(condensedBounds.get(0)); - } - default -> false; - }; - } - - - /* - * Static methods. - */ - - - // Does e represent a generic declaration? - // - // A declaration is generic if it declares one or more type parameters. - // - // Since an annotation interface cannot declare type parameters, it follows that TypeElements representing annotation - // instances are never generic. - private static final boolean generic(final Element e) { - return switch (e.getKind()) { - case CLASS, CONSTRUCTOR, ENUM, INTERFACE, METHOD, RECORD -> !((Parameterizable)e).getTypeParameters().isEmpty(); - default -> false; - }; - } - - // Is t the usage of a generic class, i.e. a usage (whether raw or parameterized) of a generic class declaration? - // - // t is deemed to be generic if it is a declared type whose defining element (its type declaration) is generic. - // - // Array types are never generic. - private static final boolean generic(final TypeMirror t) { - return - t.getKind() == TypeKind.DECLARED && - generic(((DeclaredType)t).asElement()); - } - - // Is t a parameterized type (and not a raw type) strictly according to the rules of the Java Language Specification? - // - // A type is parameterized if it is a declared type with a non-empty list of type arguments. No other type is - // parameterized. - private static final boolean parameterized(final TypeMirror t) { - return - t.getKind() == TypeKind.DECLARED && - !((DeclaredType)t).getTypeArguments().isEmpty(); - } - - // Is t a type that CDI considers to be "parameterized"? - // - // There are some cases, but not all, where CDI (incorrectly) considers an array type to be something that can be - // parameterized, or else "bean type parameter" resolution would never work. See - // https://stackoverflow.com/questions/76493672/when-cdi-speaks-of-a-parameterized-type-does-it-also-incorrectly-mean-array-typ. - // - // The semantics CDI wants to express are really: can t *yield* a raw type, for a certain definition of "yield"? See - // #yieldsRawType(TypeMirror) below. - private static final boolean cdiParameterized(final TypeMirror t) { - return yieldsRawType(t); - } - - // Can t *yield* a raw type? - // - // We say that to yield a raw type, t must be either: - // - // * an array type with a parameterized element type - // * a declared type with at least one type argument - private static final boolean yieldsRawType(final TypeMirror t) { - return parameterized(t) || t.getKind() == TypeKind.ARRAY && parameterized(elementType(t)); - } - - // Is t a raw type, following the rules of the Java Language Specification, not CDI? - // - // A raw type is either "the erasure of a parameterized type" (so List's raw type is List, clearly not - // List, and not List) or "an array type whose element type is a raw type" (so List[]'s raw type is - // List[]). (String is not a raw type; its element defines no type parameters.) - // - // No other type is a raw type. - // - // CDI gets confused and uses "raw" in different senses. The sense used here is that of the Java Language - // Specification. - private static final boolean raw(final TypeMirror t) { - return switch (t.getKind()) { - case ARRAY -> raw(elementType((ArrayType)t)); - case DECLARED -> generic(t) && ((DeclaredType)t).getTypeArguments().isEmpty(); - case TYPEVAR -> false; // called out explicitly just so you realize it - default -> false; - }; - } - - // Is t a declared type that bears the supplied fully qualified name? - private static final boolean declaredTypeNamed(final TypeMirror t, final CharSequence n) { - return - t.getKind() == TypeKind.DECLARED && - named(((DeclaredType)t), n); - } - - // Regardless of its reported TypeKind, does t's declaring TypeElement bear the supplied fully qualified name? - // - // Throws ClassCastException if the return value of t.asElement() is not a TypeElement. - private static final boolean named(final DeclaredType t, final CharSequence n) { - // (No getKind() check on purpose.) - return ((QualifiedNameable)t.asElement()).getQualifiedName().contentEquals(n); - } - - // Do all supplied TypeMirrors, when used as type arguments, pass the test represented by the supplied Predicate? - private static final boolean allTypeArgumentsAre(final Iterable typeArguments, final Predicate p) { - for (final TypeMirror t : typeArguments) { - if (!p.test(t)) { - return false; - } - } - return true; - } - - // Is e a TypeElement representing java.lang.Object? - private static final boolean isJavaLangObject(final Element e) { - return - e.getKind() == ElementKind.CLASS && - ((QualifiedNameable)e).getQualifiedName().contentEquals("java.lang.Object"); - } - - // Is t a DeclaredType whose asElement() method returns an Element representing java.lang.Object? - private static final boolean isJavaLangObject(final TypeMirror t) { - return - t.getKind() == TypeKind.DECLARED && - isJavaLangObject(((DeclaredType)t).asElement()); - } - - // Is t an "actual type"? - // - // CDI mentions actual types but does not define what they are. This method attempts to divine and implement the - // intent. - // - // A comment in a closed bug report (CDI-502) - // (https://issues.redhat.com/browse/CDI-502?focusedId=13036118&page=com.atlassian.jira.plugin.system.issuetabpanels%3Acomment-tabpanel#comment-13036118) - // by one of the reference implementation's authors (Jozef Hartinger) provides the only definition: - // - // "An actual type is a type that is not a wildcard nor [sic] an unresolved [sic] type variable." - // - // The Java Language Specification does not mention anything about "actual types" or type variable "resolution". CDI - // does not mention anything about type variable "resolution". - // - // (Clearly type variable "resolution" must simply be the process of supplying a type argument for a type parameter.) - // - // Presumably the null type is not intended to be an actual type either. - // - // More strictly, therefore, the intent seems to be that an actual type is an array, declared or primitive type, and - // none other. - // - // See also: https://github.com/weld/core/blob/5.1.2.Final/impl/src/main/java/org/jboss/weld/util/Types.java#L213 - private static final boolean actual(final TypeMirror t) { - return switch (t.getKind()) { - case ARRAY, BOOLEAN, BYTE, CHAR, DECLARED, DOUBLE, FLOAT, INT, LONG, SHORT -> true; - default -> false; - }; - } - - // Returns the element type of t. - // - // The element type of an array type is the element type of its component type. - // - // The element type of every other kind of type is the type itself. - private static final TypeMirror elementType(final TypeMirror t) { - return t.getKind() == TypeKind.ARRAY ? elementType(((ArrayType)t).getComponentType()) : t; - } - -} diff --git a/src/main/java/org/microbean/bean/Types.java b/src/main/java/org/microbean/bean/Types.java new file mode 100644 index 0000000..e8b9d83 --- /dev/null +++ b/src/main/java/org/microbean/bean/Types.java @@ -0,0 +1,264 @@ +/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- + * + * 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 + * + * 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.bean; + +import java.lang.constant.ClassDesc; +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.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Set; +import java.util.StringJoiner; + +import java.util.function.Predicate; + +import javax.lang.model.element.Element; +import javax.lang.model.element.ElementKind; +import javax.lang.model.element.Parameterizable; +import javax.lang.model.element.QualifiedNameable; + +import javax.lang.model.type.ArrayType; +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.IntersectionType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.TypeVariable; +import javax.lang.model.type.WildcardType; + +import org.microbean.construct.Domain; + +import org.microbean.construct.element.UniversalElement; + +import org.microbean.construct.type.UniversalType; + +import static java.lang.constant.ConstantDescs.BSM_INVOKE; + +import static java.util.HashSet.newHashSet; + +import static java.util.stream.Stream.concat; + +public class Types implements Constable { + + + /* + * Static fields. + */ + + + private static final ClassDesc CD_Domain = ClassDesc.of("org.microbean.construct.Domain"); + + + /* + * Instance fields. + */ + + + private final Comparator c; + + private final Domain domain; + + + /* + * Constructors. + */ + + + public Types(final Domain domain) { + super(); + this.domain = Objects.requireNonNull(domain, "domain"); + this.c = new SpecializationComparator(); + } + + + /* + * Instance methods. + */ + + + /** + * Returns an {@link Optional} housing a {@link ConstantDesc} that represents this {@link Types}. + * + *

This method never returns {@code null}.

+ * + *

The default implementation of this method relies on the presence of a {@code public} constructor that accepts a + * single {@link Domain}-typed argument.

+ * + *

The {@link Optional} returned by an invocation of this method may be, and often will be, {@linkplain + * Optional#isEmpty() empty}.

+ * + * @return an {@link Optional} housing a {@link ConstantDesc} that represents this {@link Types}; never {@code null} + * + * @see Constable#describeConstable() + */ + @Override // Constable + public Optional describeConstable() { + return (this.domain instanceof Constable c ? c.describeConstable() : Optional.empty()) + .map(domainDesc -> DynamicConstantDesc.of(BSM_INVOKE, + MethodHandleDesc.ofConstructor(ClassDesc.of(this.getClass().getName()), + CD_Domain), + domainDesc)); + } + + public final Domain domain() { + return this.domain; + } + + @Override // Object + public boolean equals(final Object other) { + if (other == this) { + return true; + } else if (other != null && other.getClass() == this.getClass()) { + return this.domain().equals(((Types)other).domain()); + } else { + return false; + } + } + + @Override // Object + public int hashCode() { + return this.domain().hashCode(); + } + + private final boolean isInterface(final UniversalElement e) { + return e.getKind().isInterface(); + } + + private final boolean isInterface(final UniversalType t) { + return t.getKind() == TypeKind.DECLARED && isInterface(t.asElement()); + } + + final String name(final TypeMirror t) { + final UniversalType ut = UniversalType.of(t, this.domain()); + return switch (ut.getKind()) { + case ARRAY -> name(ut.getComponentType()) + "[]"; + case BOOLEAN -> "boolean"; + case BYTE -> "byte"; + case CHAR -> "char"; + case DECLARED, TYPEVAR -> name(ut.asElement()); + case DOUBLE -> "double"; + case FLOAT -> "float"; + case INT -> "int"; + case INTERSECTION -> { + final StringJoiner sj = new java.util.StringJoiner("&"); + for (final UniversalType bound : ut.getBounds()) { + sj.add(name(bound)); + } + yield sj.toString(); + } + case LONG -> "long"; + case SHORT -> "short"; + default -> ut.toString(); + }; + } + + public final List supertypes(final TypeMirror t) { + return this.supertypes(t, Types::returnTrue); + } + + public final List supertypes(final TypeMirror t, final Predicate p) { + final ArrayList nonInterfaceTypes = new ArrayList<>(7); // arbitrary size + final ArrayList interfaceTypes = new ArrayList<>(17); // arbitrary size + supertypes(UniversalType.of(t, this.domain()), p, nonInterfaceTypes, interfaceTypes, newHashSet(13)); // arbitrary size + nonInterfaceTypes.trimToSize(); + interfaceTypes.trimToSize(); + return + concat(nonInterfaceTypes.stream(), // non-interface supertypes are already sorted from most-specific to least + interfaceTypes.stream().sorted(this.c)) // have to sort interfaces because you can extend them in any order + .toList(); + } + + private final void supertypes(final UniversalType t, + final Predicate p, + final ArrayList nonInterfaceTypes, + final ArrayList interfaceTypes, + final Set seen) { + if (seen.add(name(t))) { + if (p.test(t)) { + if (isInterface(t)) { + interfaceTypes.add(t); // reflexive + } else { + nonInterfaceTypes.add(t); // reflexive + } + } + for (final TypeMirror directSupertype : domain.directSupertypes(t)) { + this.supertypes(UniversalType.of(directSupertype, this.domain()), p, nonInterfaceTypes, interfaceTypes, seen); + } + } + } + + /* + * Static methods. + */ + + + private static final String name(final Element e) { + return e instanceof QualifiedNameable qn ? name(qn) : name(e.getSimpleName()); + } + + private static final String name(final QualifiedNameable qn) { + final CharSequence n = qn.getQualifiedName(); + return n == null || n.isEmpty() ? name(qn.getSimpleName()) : name(n); + } + + private static final String name(final CharSequence cs) { + return cs instanceof String s ? s : cs.toString(); + } + + private static final boolean returnTrue(final T ignored) { + return true; + } + + + /* + * Inner and nested classes. + */ + + + private final class SpecializationComparator implements Comparator { + + private SpecializationComparator() { + super(); + } + + @Override + public final int compare(final UniversalType t, final UniversalType s) { + if (t == s) { + return 0; + } else if (t == null) { + return 1; // nulls right + } else if (s == null) { + return -1; // nulls right + } else if (domain.sameType(t, s)) { + return 0; + } else if (domain.subtype(t, s)) { + // t is a subtype of s; s is a proper supertype of t + return -1; + } else if (domain.subtype(s, t)) { + // s is a subtype of t; t is a proper supertype of s + return 1; + } else { + return name(t).compareTo(name(s)); + } + } + + } + +} diff --git a/src/main/java/org/microbean/bean/model/Trees.java b/src/main/java/org/microbean/bean/model/Trees.java index 5873c0e..5a611c8 100644 --- a/src/main/java/org/microbean/bean/model/Trees.java +++ b/src/main/java/org/microbean/bean/model/Trees.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 @@ -29,8 +29,8 @@ private Trees() { } /** - * Returns a {@link Stream} of nodes in breadth-first order from a root node of a notional (and definitionally acyclic) tree and a means for - * acquiring its immediate notional children. + * Returns a {@link Stream} of nodes in breadth-first order from a root node of a notional (and definitionally + * acyclic) tree and a means for acquiring its immediate notional children. * *

No checks are performed for cycles in the resulting graph which means that if the supplied {@link Function} is * not well-behaved undefined behavior can result, including infinite loops.

@@ -59,8 +59,8 @@ static final Stream streamBreadthFirst(final N element, final FunctionNo checks are performed for cycles in the resulting graph which means that if the supplied {@link Function} is * not well-behaved undefined behavior can result, including infinite loops.

diff --git a/src/site/markdown/factories.md b/src/site/markdown/factories.md deleted file mode 100644 index 9e397e0..0000000 --- a/src/site/markdown/factories.md +++ /dev/null @@ -1,50 +0,0 @@ -# Factories - -When turning something like this: - -``` -@ApplicationScoped -@C -public class A { - - private B b; - - public A() { - super(); - } - - @Inject - public void setB(B b) { - this.b = b; - } - -} -``` -…into a `Factory`: -* The _instance type_ is `A` -* The _bean types_ are those of the transitive type closure of the instance type -* The _instance qualifiers_ are `C` -* The _factory class_ is `A` -* The _factory qualifiers_ are `C` -* The _factory executable_ is `A()` -* The _factory executable variables_ are none. -* The _initializer variables_ are the `B`-typed parameter of the `setB(B)` _initializer method_ -* The _disposer executable_ is `null` -* The _disposer executable variables_ are none. - -Old Byte Buddy-related example: -``` -public BBFactoryClassGenerator(final Type productType, - final BeanId beanId, - final Class productFactoryClass, // nullable - final Set productFactoryQualifiers, // nullable - MethodDescription.Token producerToken, // nullable - final List producerDependencies, - Map, ? extends List> initializerDependencies, - final Interceptions interceptions, - final MethodDescription.Token disposerToken, // nullable - final List disposerDependencies, - final int disposedParameterIndex, // can be negative - final Path directoryForGeneratedClasses) { // nullable -``` - diff --git a/src/site/markdown/stacks.md b/src/site/markdown/stacks.md deleted file mode 100644 index 0245673..0000000 --- a/src/site/markdown/stacks.md +++ /dev/null @@ -1,651 +0,0 @@ -``` -Compilation started at Thu Jul 6 14:51:00 - -JAVA_HOME=$(/usr/libexec/java_home -v20) mvn -Dstyle.color=never -Dorg.slf4j.simpleLogger.defaultLogLevel=info -s ~/.m2/settings-no-proxies.xml -f "$(findup pom.xml)" test -o -[INFO] Scanning for projects... -[INFO] Inspecting build with total of 1 modules... -[INFO] Installing Nexus Staging features: -[INFO] ... total of 1 executions of maven-deploy-plugin replaced with nexus-staging-maven-plugin -[INFO] -[INFO] --------------------< org.microbean:microbean-bean >-------------------- -[INFO] Building microBean™ Bean 0.0.1-SNAPSHOT -[INFO] --------------------------------[ jar ]--------------------------------- -[INFO] -[INFO] --- maven-toolchains-plugin:3.1.0:toolchain (default) @ microbean-bean --- -[INFO] Required toolchain: jdk [ vendor='openjdk' version='20' ] -[INFO] Found matching toolchain for type jdk: JDK[/Library/Java/JavaVirtualMachines/jdk-20.jdk/Contents/Home] -[INFO] -[INFO] --- maven-resources-plugin:3.3.1:resources (default-resources) @ microbean-bean --- -[INFO] skip non existing resourceDirectory /Users/lairdnelson/Projects/github/microbean/microbean-bean/src/main/resources -[INFO] Copying 1 resource from to target/classes/META-INF -[INFO] -[INFO] --- maven-compiler-plugin:3.11.0:compile (default-compile) @ microbean-bean --- -[INFO] Toolchain in maven-compiler-plugin: JDK[/Library/Java/JavaVirtualMachines/jdk-20.jdk/Contents/Home] -[INFO] Nothing to compile - all classes are up to date -[INFO] -[INFO] --- maven-resources-plugin:3.3.1:testResources (default-testResources) @ microbean-bean --- -[INFO] skip non existing resourceDirectory /Users/lairdnelson/Projects/github/microbean/microbean-bean/src/test/resources -[INFO] -[INFO] --- maven-compiler-plugin:3.11.0:testCompile (default-testCompile) @ microbean-bean --- -[INFO] Toolchain in maven-compiler-plugin: JDK[/Library/Java/JavaVirtualMachines/jdk-20.jdk/Contents/Home] -[INFO] Nothing to compile - all classes are up to date -[INFO] -[INFO] --- maven-surefire-plugin:3.0.0:test (default-test) @ microbean-bean --- -[INFO] Toolchain in maven-surefire-plugin: JDK[/Library/Java/JavaVirtualMachines/jdk-20.jdk/Contents/Home] -[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider -[INFO] -[INFO] ------------------------------------------------------- -[INFO] T E S T S -[INFO] ------------------------------------------------------- -[INFO] Running org.microbean.bean.TestDefaultBeanSet -[INFO] Running org.microbean.bean.TestConstableSemantics -[INFO] Running org.microbean.bean.TestMethodHandlesLookup -[INFO] Running org.microbean.bean.TestReferenceTypeList -[INFO] Running org.microbean.bean.TestFactory -[INFO] Running org.microbean.bean.TestQualifiers -[INFO] Running org.microbean.bean.TestJlm -[INFO] Running org.microbean.bean.TestSelector -[WARNING] Tests run: 1, Failures: 0, Errors: 0, Skipped: 1, Time elapsed: 0.022 s - in org.microbean.bean.TestMethodHandlesLookup -[INFO] Running org.microbean.bean.TestConstantDescs -[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.025 s - in org.microbean.bean.TestFactory -[INFO] Running org.microbean.bean.TestBean -[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.027 s - in org.microbean.bean.TestQualifiers -[INFO] Running org.microbean.bean.TestConcurrentHashMapComputation -[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.004 s - in org.microbean.bean.TestConcurrentHashMapComputation -[INFO] Tests run: 0, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.007 s - in org.microbean.bean.TestConstantDescs -[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.211 s - in org.microbean.bean.TestJlm -*** supertype of T: none -*** supertype of S: java.lang.String -*** types: [java.io.Serializable] -java.lang.Exception: Stack trace - at java.base/java.lang.Thread.dumpStack(Thread.java:2246) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang.declaredType(Lang.java:1373) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang$ConstableTypeAndElementSource.declaredType(Lang.java:2016) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.TypeAndElementSource.declaredType(TypeAndElementSource.java:56) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.bean(DefaultBeanSet.java:221) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:177) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:137) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:109) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.TestDefaultBeanSet.setup(TestDefaultBeanSet.java:74) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) - at java.base/java.lang.reflect.Method.invoke(Method.java:578) - at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727) - at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) - at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptLifecycleMethod(TimeoutExtension.java:128) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptBeforeEachMethod(TimeoutExtension.java:78) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeMethodInExtensionContext(ClassBasedTestDescriptor.java:520) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$synthesizeBeforeEachMethodAdapter$23(ClassBasedTestDescriptor.java:505) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeEachMethods$3(TestMethodTestDescriptor.java:174) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeMethodsOrCallbacksUntilExceptionOccurs$6(TestMethodTestDescriptor.java:202) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeMethodsOrCallbacksUntilExceptionOccurs(TestMethodTestDescriptor.java:202) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeEachMethods(TestMethodTestDescriptor.java:171) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:134) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) - at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) - at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) - at java.base/java.util.concurrent.ForkJoinPool.helpJoin(ForkJoinPool.java:2076) - at java.base/java.util.concurrent.ForkJoinTask.awaitDone(ForkJoinTask.java:423) - at java.base/java.util.concurrent.ForkJoinTask.join(ForkJoinTask.java:651) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.joinConcurrentTasksInReverseOrderToEnableWorkStealing(ForkJoinPoolHierarchicalTestExecutorService.java:179) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.invokeAll(ForkJoinPoolHierarchicalTestExecutorService.java:153) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) - at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) - at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) - at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312) - at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843) - at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808) - at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188) -java.lang.Exception: Stack trace - at java.base/java.lang.Thread.dumpStack(Thread.java:2246) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang.declaredType(Lang.java:1373) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang$ConstableTypeAndElementSource.declaredType(Lang.java:2016) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.TypeAndElementSource.declaredType(TypeAndElementSource.java:56) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.bean(DefaultBeanSet.java:221) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:177) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:137) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:109) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.TestDefaultBeanSet.setup(TestDefaultBeanSet.java:74) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) - at java.base/java.lang.reflect.Method.invoke(Method.java:578) - at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727) - at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) - at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptLifecycleMethod(TimeoutExtension.java:128) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptBeforeEachMethod(TimeoutExtension.java:78) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeMethodInExtensionContext(ClassBasedTestDescriptor.java:520) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$synthesizeBeforeEachMethodAdapter$23(ClassBasedTestDescriptor.java:505) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeEachMethods$3(TestMethodTestDescriptor.java:174) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeMethodsOrCallbacksUntilExceptionOccurs$6(TestMethodTestDescriptor.java:202) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeMethodsOrCallbacksUntilExceptionOccurs(TestMethodTestDescriptor.java:202) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeEachMethods(TestMethodTestDescriptor.java:171) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:134) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) - at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) - at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) - at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.tryRemoveAndExec(ForkJoinPool.java:1351) - at java.base/java.util.concurrent.ForkJoinTask.awaitDone(ForkJoinTask.java:422) - at java.base/java.util.concurrent.ForkJoinTask.join(ForkJoinTask.java:651) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.joinConcurrentTasksInReverseOrderToEnableWorkStealing(ForkJoinPoolHierarchicalTestExecutorService.java:179) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.invokeAll(ForkJoinPoolHierarchicalTestExecutorService.java:153) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) - at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) - at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) - at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312) - at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843) - at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808) - at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188) -java.lang.Exception: Stack trace -[INFO] Tests run: 8, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.228 s - in org.microbean.bean.TestBean - at java.base/java.lang.Thread.dumpStack(Thread.java:2246) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang.declaredType(Lang.java:1373) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang$ConstableTypeAndElementSource.declaredType(Lang.java:2016) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.TypeAndElementSource.declaredType(TypeAndElementSource.java:56) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.bean(DefaultBeanSet.java:223) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:177) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:137) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:109) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.TestDefaultBeanSet.setup(TestDefaultBeanSet.java:74) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) - at java.base/java.lang.reflect.Method.invoke(Method.java:578) - at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727) - at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) - at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptLifecycleMethod(TimeoutExtension.java:128) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptBeforeEachMethod(TimeoutExtension.java:78) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeMethodInExtensionContext(ClassBasedTestDescriptor.java:520) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$synthesizeBeforeEachMethodAdapter$23(ClassBasedTestDescriptor.java:505) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeEachMethods$3(TestMethodTestDescriptor.java:174) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeMethodsOrCallbacksUntilExceptionOccurs$6(TestMethodTestDescriptor.java:202) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeMethodsOrCallbacksUntilExceptionOccurs(TestMethodTestDescriptor.java:202) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeEachMethods(TestMethodTestDescriptor.java:171) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:134) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) - at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) - at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) - at java.base/java.util.concurrent.ForkJoinPool.helpJoin(ForkJoinPool.java:2076) - at java.base/java.util.concurrent.ForkJoinTask.awaitDone(ForkJoinTask.java:423) - at java.base/java.util.concurrent.ForkJoinTask.join(ForkJoinTask.java:651) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.joinConcurrentTasksInReverseOrderToEnableWorkStealing(ForkJoinPoolHierarchicalTestExecutorService.java:179) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.invokeAll(ForkJoinPoolHierarchicalTestExecutorService.java:153) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) - at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) - at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) - at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312) - at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843) - at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808) - at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188) -java.lang.Exception: Stack trace - at java.base/java.lang.Thread.dumpStack(Thread.java:2246) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang.declaredType(Lang.java:1373) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang$ConstableTypeAndElementSource.declaredType(Lang.java:2016) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.TypeAndElementSource.declaredType(TypeAndElementSource.java:56) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.bean(DefaultBeanSet.java:223) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:177) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:137) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:109) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.TestDefaultBeanSet.setup(TestDefaultBeanSet.java:74) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) - at java.base/java.lang.reflect.Method.invoke(Method.java:578) - at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727) - at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) - at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptLifecycleMethod(TimeoutExtension.java:128) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptBeforeEachMethod(TimeoutExtension.java:78) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeMethodInExtensionContext(ClassBasedTestDescriptor.java:520) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$synthesizeBeforeEachMethodAdapter$23(ClassBasedTestDescriptor.java:505) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeEachMethods$3(TestMethodTestDescriptor.java:174) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeMethodsOrCallbacksUntilExceptionOccurs$6(TestMethodTestDescriptor.java:202) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeMethodsOrCallbacksUntilExceptionOccurs(TestMethodTestDescriptor.java:202) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeEachMethods(TestMethodTestDescriptor.java:171) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:134) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) - at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) - at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) - at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.tryRemoveAndExec(ForkJoinPool.java:1351) - at java.base/java.util.concurrent.ForkJoinTask.awaitDone(ForkJoinTask.java:422) - at java.base/java.util.concurrent.ForkJoinTask.join(ForkJoinTask.java:651) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.joinConcurrentTasksInReverseOrderToEnableWorkStealing(ForkJoinPoolHierarchicalTestExecutorService.java:179) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.invokeAll(ForkJoinPoolHierarchicalTestExecutorService.java:153) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) - at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) - at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) - at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312) - at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843) - at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808) - at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188) -*** rtl types: [T, java.lang.constant.ClassDesc, java.lang.invoke.TypeDescriptor.OfField, java.lang.constant.ConstantDesc, java.lang.invoke.TypeDescriptor] -[ERROR] Tests run: 4, Failures: 0, Errors: 2, Skipped: 0, Time elapsed: 0.259 s <<< FAILURE! - in org.microbean.bean.TestDefaultBeanSet -[ERROR] org.microbean.bean.TestDefaultBeanSet.testConstableStuff Time elapsed: 0.028 s <<< ERROR! -java.lang.NullPointerException: Cannot invoke "javax.lang.model.type.TypeMirror.toString()" because "t" is null - at jdk.compiler/com.sun.tools.javac.model.JavacTypes.getDeclaredType0(JavacTypes.java:272) - at jdk.compiler/com.sun.tools.javac.model.JavacTypes.getDeclaredType(JavacTypes.java:241) - at jdk.compiler/com.sun.tools.javac.model.JavacTypes.getDeclaredType(JavacTypes.java:249) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang.declaredType(Lang.java:1387) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang$ConstableTypeAndElementSource.declaredType(Lang.java:2016) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.bean(DefaultBeanSet.java:223) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:177) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:137) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:109) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.TestDefaultBeanSet.setup(TestDefaultBeanSet.java:74) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) - at java.base/java.lang.reflect.Method.invoke(Method.java:578) - at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727) - at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) - at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptLifecycleMethod(TimeoutExtension.java:128) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptBeforeEachMethod(TimeoutExtension.java:78) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeMethodInExtensionContext(ClassBasedTestDescriptor.java:520) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$synthesizeBeforeEachMethodAdapter$23(ClassBasedTestDescriptor.java:505) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeEachMethods$3(TestMethodTestDescriptor.java:174) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeMethodsOrCallbacksUntilExceptionOccurs$6(TestMethodTestDescriptor.java:202) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeMethodsOrCallbacksUntilExceptionOccurs(TestMethodTestDescriptor.java:202) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeEachMethods(TestMethodTestDescriptor.java:171) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:134) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) - at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) - at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) - at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.tryRemoveAndExec(ForkJoinPool.java:1351) - at java.base/java.util.concurrent.ForkJoinTask.awaitDone(ForkJoinTask.java:422) - at java.base/java.util.concurrent.ForkJoinTask.join(ForkJoinTask.java:651) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.joinConcurrentTasksInReverseOrderToEnableWorkStealing(ForkJoinPoolHierarchicalTestExecutorService.java:179) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.invokeAll(ForkJoinPoolHierarchicalTestExecutorService.java:153) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) - at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) - at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) - at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312) - at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843) - at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808) - at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188) - -[ERROR] org.microbean.bean.TestDefaultBeanSet.testBeans Time elapsed: 0.02 s <<< ERROR! -java.lang.NullPointerException: Cannot invoke "javax.lang.model.type.TypeMirror.toString()" because "t" is null - at jdk.compiler/com.sun.tools.javac.model.JavacTypes.getDeclaredType0(JavacTypes.java:272) - at jdk.compiler/com.sun.tools.javac.model.JavacTypes.getDeclaredType(JavacTypes.java:241) - at jdk.compiler/com.sun.tools.javac.model.JavacTypes.getDeclaredType(JavacTypes.java:249) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang.declaredType(Lang.java:1387) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.Lang$ConstableTypeAndElementSource.declaredType(Lang.java:2016) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.bean(DefaultBeanSet.java:223) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:177) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:137) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:109) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.TestDefaultBeanSet.setup(TestDefaultBeanSet.java:74) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) - at java.base/java.lang.reflect.Method.invoke(Method.java:578) - at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727) - at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) - at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptLifecycleMethod(TimeoutExtension.java:128) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptBeforeEachMethod(TimeoutExtension.java:78) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeMethodInExtensionContext(ClassBasedTestDescriptor.java:520) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$synthesizeBeforeEachMethodAdapter$23(ClassBasedTestDescriptor.java:505) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeEachMethods$3(TestMethodTestDescriptor.java:174) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeMethodsOrCallbacksUntilExceptionOccurs$6(TestMethodTestDescriptor.java:202) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeMethodsOrCallbacksUntilExceptionOccurs(TestMethodTestDescriptor.java:202) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeEachMethods(TestMethodTestDescriptor.java:171) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:134) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) - at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) - at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) - at java.base/java.util.concurrent.ForkJoinPool.helpJoin(ForkJoinPool.java:2076) - at java.base/java.util.concurrent.ForkJoinTask.awaitDone(ForkJoinTask.java:423) - at java.base/java.util.concurrent.ForkJoinTask.join(ForkJoinTask.java:651) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.joinConcurrentTasksInReverseOrderToEnableWorkStealing(ForkJoinPoolHierarchicalTestExecutorService.java:179) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService.invokeAll(ForkJoinPoolHierarchicalTestExecutorService.java:153) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:155) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) - at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) - at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) - at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312) - at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843) - at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808) - at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188) - -*** rtl types: [S, java.lang.String, java.lang.Object, java.io.Serializable, java.lang.CharSequence, java.lang.Comparable, java.lang.constant.Constable, java.lang.constant.ConstantDesc] -[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.265 s - in org.microbean.bean.TestSelector -[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.265 s - in org.microbean.bean.TestConstableSemantics -[INFO] Tests run: 0, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.267 s - in org.microbean.bean.TestReferenceTypeList -[INFO] -[INFO] Results: -[INFO] -[ERROR] Errors: -[ERROR] TestDefaultBeanSet.setup:74 » NullPointer Cannot invoke "javax.lang.model.type.TypeMirror.toString()" because "t" is null -[ERROR] TestDefaultBeanSet.setup:74 » NullPointer Cannot invoke "javax.lang.model.type.TypeMirror.toString()" because "t" is null -[INFO] -[ERROR] Tests run: 23, Failures: 0, Errors: 2, Skipped: 1 -[INFO] -[INFO] ------------------------------------------------------------------------ -[INFO] BUILD FAILURE -[INFO] ------------------------------------------------------------------------ -[INFO] Total time: 4.273 s -[INFO] Finished at: 2023-07-06T14:51:05-07:00 -[INFO] ------------------------------------------------------------------------ -[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:3.0.0:test (default-test) on project microbean-bean: -[ERROR] -[ERROR] Please refer to /Users/lairdnelson/Projects/github/microbean/microbean-bean/target/surefire-reports for the individual test results. -[ERROR] Please refer to dump files (if any exist) [date].dump, [date]-jvmRun[N].dump and [date].dumpstream. -[ERROR] -> [Help 1] -[ERROR] -[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch. -[ERROR] Re-run Maven using the -X switch to enable full debug logging. -[ERROR] -[ERROR] For more information about the errors and possible solutions, please read the following articles: -[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException - -Compilation exited abnormally with code 1 at Thu Jul 6 14:51:05 - -VERY intermittent: - -Compilation started at Mon Jul 24 14:11:53 - -JAVA_HOME=$(/usr/libexec/java_home -v20) mvn -Dstyle.color=never -Dorg.slf4j.simpleLogger.defaultLogLevel=info -s ~/.m2/settings-no-proxies.xml -f "$(findup pom.xml)" clean install javadoc:javadoc -[INFO] Scanning for projects... -[INFO] Inspecting build with total of 1 modules... -[INFO] Installing Nexus Staging features: -[INFO] ... total of 1 executions of maven-deploy-plugin replaced with nexus-staging-maven-plugin -[INFO] -[INFO] --------------------< org.microbean:microbean-bean >-------------------- -[INFO] Building microBean™ Bean 0.0.1-SNAPSHOT -[INFO] --------------------------------[ jar ]--------------------------------- -[INFO] -[INFO] --- maven-clean-plugin:3.2.0:clean (default-clean) @ microbean-bean --- -[INFO] Deleting /Users/lairdnelson/Projects/github/microbean/microbean-bean/target -[INFO] Deleting /Users/lairdnelson/Projects/github/microbean/microbean-bean (includes = [src/**/*~, *~], excludes = []) -[INFO] -[INFO] --- maven-toolchains-plugin:3.1.0:toolchain (default) @ microbean-bean --- -[INFO] Required toolchain: jdk [ vendor='openjdk' version='20' ] -[INFO] Found matching toolchain for type jdk: JDK[/Library/Java/JavaVirtualMachines/jdk-20.jdk/Contents/Home] -[INFO] -[INFO] --- maven-resources-plugin:3.3.1:resources (default-resources) @ microbean-bean --- -[INFO] skip non existing resourceDirectory /Users/lairdnelson/Projects/github/microbean/microbean-bean/src/main/resources -[INFO] Copying 1 resource from to target/classes/META-INF -[INFO] -[INFO] --- maven-compiler-plugin:3.11.0:compile (default-compile) @ microbean-bean --- -[INFO] Toolchain in maven-compiler-plugin: JDK[/Library/Java/JavaVirtualMachines/jdk-20.jdk/Contents/Home] -[INFO] Changes detected - recompiling the module! :source -[INFO] Compiling 71 source files with javac [forked debug deprecation release 20 module-path] to target/classes -[INFO] -[INFO] --- maven-resources-plugin:3.3.1:testResources (default-testResources) @ microbean-bean --- -[INFO] skip non existing resourceDirectory /Users/lairdnelson/Projects/github/microbean/microbean-bean/src/test/resources -[INFO] -[INFO] --- maven-compiler-plugin:3.11.0:testCompile (default-testCompile) @ microbean-bean --- -[INFO] Toolchain in maven-compiler-plugin: JDK[/Library/Java/JavaVirtualMachines/jdk-20.jdk/Contents/Home] -[INFO] Changes detected - recompiling the module! :dependency -[INFO] Compiling 12 source files with javac [forked debug deprecation release 20 module-path] to target/test-classes -[INFO] -[INFO] --- maven-surefire-plugin:3.0.0:test (default-test) @ microbean-bean --- -[INFO] Toolchain in maven-surefire-plugin: JDK[/Library/Java/JavaVirtualMachines/jdk-20.jdk/Contents/Home] -[INFO] Using auto detected provider org.apache.maven.surefire.junitplatform.JUnitPlatformProvider -[INFO] -[INFO] ------------------------------------------------------- -[INFO] T E S T S -[INFO] ------------------------------------------------------- -[INFO] Running org.microbean.bean.TestConstableSemantics -[INFO] Running org.microbean.bean.TestMethodHandlesLookup -[INFO] Running org.microbean.bean2.TestFactory -[INFO] Running org.microbean.bean.TestDefaultBeanSet -[INFO] Running org.microbean.bean.TestJlm -[INFO] Running org.microbean.bean.TestQualifiers -[INFO] Running org.microbean.bean.TestSelector -[INFO] Running org.microbean.bean.TestFactory -[WARNING] Tests run: 1, Failures: 0, Errors: 0, Skipped: 1, Time elapsed: 0.022 s - in org.microbean.bean.TestMethodHandlesLookup -[INFO] Running org.microbean.bean.TestReferenceTypeList -[INFO] Tests run: 3, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.026 s - in org.microbean.bean2.TestFactory -[INFO] Running org.microbean.bean.TestConstantDescs -[INFO] Tests run: 0, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.028 s - in org.microbean.bean.TestFactory -[INFO] Running org.microbean.bean.TestBean -[INFO] Tests run: 0, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.028 s - in org.microbean.bean.TestQualifiers -[INFO] Running org.microbean.bean.TestConcurrentHashMapComputation -[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.002 s - in org.microbean.bean.TestConcurrentHashMapComputation -[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.015 s - in org.microbean.bean.TestConstantDescs -[INFO] Tests run: 2, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.246 s - in org.microbean.bean.TestJlm -[INFO] Tests run: 4, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.262 s - in org.microbean.bean.TestSelector -*** supertype of T: none -*** types: [java.io.Serializable] -*** supertype of S: java.lang.String -[INFO] Tests run: 5, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.259 s - in org.microbean.bean.TestBean -*** rtl types: [T, java.lang.constant.ClassDesc, java.lang.invoke.TypeDescriptor.OfField, java.lang.constant.ConstantDesc, java.lang.invoke.TypeDescriptor] -*** rtl types: [S, java.lang.String, java.lang.Object, java.io.Serializable, java.lang.CharSequence, java.lang.Comparable, java.lang.constant.Constable, java.lang.constant.ConstantDesc] -[ERROR] Tests run: 6, Failures: 1, Errors: 0, Skipped: 0, Time elapsed: 0.279 s <<< FAILURE! - in org.microbean.bean.TestReferenceTypeList -[ERROR] org.microbean.bean.TestDefaultBeanSet.testTypeElementResolution Time elapsed: 0.004 s <<< FAILURE! -java.lang.AssertionError: null type element for module org.microbean.bean and org.microbean.bean.Alternate - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.TypeAndElementSource.typeElement(TypeAndElementSource.java:136) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.TypeAndElementSource.declaredType(TypeAndElementSource.java:61) - at org.microbean.lang@0.0.1-SNAPSHOT/org.microbean.lang.TypeAndElementSource.declaredType(TypeAndElementSource.java:61) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:184) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:137) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.DefaultBeanSet.(DefaultBeanSet.java:109) - at org.microbean.bean@0.0.1-SNAPSHOT/org.microbean.bean.TestDefaultBeanSet.setup(TestDefaultBeanSet.java:77) - at java.base/jdk.internal.reflect.DirectMethodHandleAccessor.invoke(DirectMethodHandleAccessor.java:104) - at java.base/java.lang.reflect.Method.invoke(Method.java:578) - at org.junit.platform.commons.util.ReflectionUtils.invokeMethod(ReflectionUtils.java:727) - at org.junit.jupiter.engine.execution.MethodInvocation.proceed(MethodInvocation.java:60) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$ValidatingInvocation.proceed(InvocationInterceptorChain.java:131) - at org.junit.jupiter.engine.extension.TimeoutExtension.intercept(TimeoutExtension.java:156) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptLifecycleMethod(TimeoutExtension.java:128) - at org.junit.jupiter.engine.extension.TimeoutExtension.interceptBeforeEachMethod(TimeoutExtension.java:78) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker$ReflectiveInterceptorCall.lambda$ofVoidMethod$0(InterceptingExecutableInvoker.java:103) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.lambda$invoke$0(InterceptingExecutableInvoker.java:93) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain$InterceptedInvocation.proceed(InvocationInterceptorChain.java:106) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.proceed(InvocationInterceptorChain.java:64) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.chainAndInvoke(InvocationInterceptorChain.java:45) - at org.junit.jupiter.engine.execution.InvocationInterceptorChain.invoke(InvocationInterceptorChain.java:37) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:92) - at org.junit.jupiter.engine.execution.InterceptingExecutableInvoker.invoke(InterceptingExecutableInvoker.java:86) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.invokeMethodInExtensionContext(ClassBasedTestDescriptor.java:520) - at org.junit.jupiter.engine.descriptor.ClassBasedTestDescriptor.lambda$synthesizeBeforeEachMethodAdapter$23(ClassBasedTestDescriptor.java:505) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeEachMethods$3(TestMethodTestDescriptor.java:174) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.lambda$invokeBeforeMethodsOrCallbacksUntilExceptionOccurs$6(TestMethodTestDescriptor.java:202) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeMethodsOrCallbacksUntilExceptionOccurs(TestMethodTestDescriptor.java:202) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.invokeBeforeEachMethods(TestMethodTestDescriptor.java:171) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:134) - at org.junit.jupiter.engine.descriptor.TestMethodTestDescriptor.execute(TestMethodTestDescriptor.java:68) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$6(NodeTestTask.java:151) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$8(NodeTestTask.java:141) - at org.junit.platform.engine.support.hierarchical.Node.around(Node.java:137) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.lambda$executeRecursively$9(NodeTestTask.java:139) - at org.junit.platform.engine.support.hierarchical.ThrowableCollector.execute(ThrowableCollector.java:73) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.executeRecursively(NodeTestTask.java:138) - at org.junit.platform.engine.support.hierarchical.NodeTestTask.execute(NodeTestTask.java:95) - at org.junit.platform.engine.support.hierarchical.ForkJoinPoolHierarchicalTestExecutorService$ExclusiveTask.compute(ForkJoinPoolHierarchicalTestExecutorService.java:202) - at java.base/java.util.concurrent.RecursiveAction.exec(RecursiveAction.java:194) - at java.base/java.util.concurrent.ForkJoinTask.doExec(ForkJoinTask.java:387) - at java.base/java.util.concurrent.ForkJoinPool$WorkQueue.topLevelExec(ForkJoinPool.java:1312) - at java.base/java.util.concurrent.ForkJoinPool.scan(ForkJoinPool.java:1843) - at java.base/java.util.concurrent.ForkJoinPool.runWorker(ForkJoinPool.java:1808) - at java.base/java.util.concurrent.ForkJoinWorkerThread.run(ForkJoinWorkerThread.java:188) - -[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.309 s - in org.microbean.bean.TestConstableSemantics -[INFO] Tests run: 1, Failures: 0, Errors: 0, Skipped: 0, Time elapsed: 0.316 s - in org.microbean.bean.TestDefaultBeanSet -[INFO] -[INFO] Results: -[INFO] -[ERROR] Failures: -[ERROR] TestDefaultBeanSet.setup:77 null type element for module org.microbean.bean and org.microbean.bean.Alternate -[INFO] -[ERROR] Tests run: 25, Failures: 1, Errors: 0, Skipped: 1 -[INFO] -[INFO] ------------------------------------------------------------------------ -[INFO] BUILD FAILURE -[INFO] ------------------------------------------------------------------------ -[INFO] Total time: 3.678 s -[INFO] Finished at: 2023-07-24T14:11:58-07:00 -[INFO] ------------------------------------------------------------------------ -[ERROR] Failed to execute goal org.apache.maven.plugins:maven-surefire-plugin:3.0.0:test (default-test) on project microbean-bean: There are test failures. -[ERROR] -[ERROR] Please refer to /Users/lairdnelson/Projects/github/microbean/microbean-bean/target/surefire-reports for the individual test results. -[ERROR] Please refer to dump files (if any exist) [date].dump, [date]-jvmRun[N].dump and [date].dumpstream. -[ERROR] -> [Help 1] -[ERROR] -[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch. -[ERROR] Re-run Maven using the -X switch to enable full debug logging. -[ERROR] -[ERROR] For more information about the errors and possible solutions, please read the following articles: -[ERROR] [Help 1] http://cwiki.apache.org/confluence/display/MAVEN/MojoFailureException - -Compilation exited abnormally with code 1 at Mon Jul 24 14:11:58 - -``` diff --git a/src/site/site.xml b/src/site/site.xml index 3db653f..fb69fe9 100644 --- a/src/site/site.xml +++ b/src/site/site.xml @@ -1,23 +1,20 @@ - - - μb ${project.artifactId} - https://avatars0.githubusercontent.com/u/25515632?s=60 - ${project.url} + + + + org.apache.maven.skins maven-fluido-skin - 1.10.0 + 2.0.0 - @@ -26,12 +23,10 @@ - true false - - + diff --git a/src/test/java/logging.properties b/src/test/java/logging.properties index bb0a6fb..0993ee0 100644 --- a/src/test/java/logging.properties +++ b/src/test/java/logging.properties @@ -2,4 +2,4 @@ handlers = java.util.logging.ConsoleHandler java.util.logging.ConsoleHandler.formatter=java.util.logging.SimpleFormatter java.util.logging.ConsoleHandler.level = FINER -org.microbean.lang.level = FINE +org.microbean.construct.level = INFO diff --git a/src/test/java/org/microbean/bean/ExperimentalBeforeAllCallback.java b/src/test/java/org/microbean/bean/ExperimentalBeforeAllCallback.java new file mode 100644 index 0000000..2995e29 --- /dev/null +++ b/src/test/java/org/microbean/bean/ExperimentalBeforeAllCallback.java @@ -0,0 +1,31 @@ +/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- + * + * Copyright © 2024 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.bean; + +import org.junit.jupiter.api.extension.BeforeAllCallback; +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.TestInstancePreConstructCallback; +import org.junit.jupiter.api.extension.TestInstanceFactoryContext; + +public class ExperimentalBeforeAllCallback implements BeforeAllCallback { + + public ExperimentalBeforeAllCallback() { + super(); + } + + public final void beforeAll(final ExtensionContext ec) throws Exception { + System.out.println("*** beforeAll called"); + } + +} diff --git a/src/test/java/org/microbean/bean/ExperimentalBeforeEachCallback.java b/src/test/java/org/microbean/bean/ExperimentalBeforeEachCallback.java new file mode 100644 index 0000000..cbac73e --- /dev/null +++ b/src/test/java/org/microbean/bean/ExperimentalBeforeEachCallback.java @@ -0,0 +1,53 @@ +/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- + * + * Copyright © 2024 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.bean; + +import java.util.concurrent.atomic.AtomicBoolean; + +import org.junit.jupiter.api.TestInstance.Lifecycle; + +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.BeforeEachCallback; + +public class ExperimentalBeforeEachCallback implements BeforeEachCallback { + + private final AtomicBoolean initialized; + + public ExperimentalBeforeEachCallback() { + super(); + this.initialized = new AtomicBoolean(); + } + + @Override + public final void beforeEach(final ExtensionContext extensionContext) { + final Lifecycle l = extensionContext.getTestInstanceLifecycle().orElseThrow(); + switch (l) { + case PER_METHOD: + apply(extensionContext); + break; + case PER_CLASS: + if (initialized.compareAndSet(false, true)) { + apply(extensionContext); + } + break; + default: + throw new AssertionError(); + } + } + + protected void apply(final ExtensionContext extensionContext) { + System.out.println("*** apply called"); + } + +} diff --git a/src/test/java/org/microbean/bean/ExperimentalPreConstructCallback.java b/src/test/java/org/microbean/bean/ExperimentalPreConstructCallback.java new file mode 100644 index 0000000..98ca131 --- /dev/null +++ b/src/test/java/org/microbean/bean/ExperimentalPreConstructCallback.java @@ -0,0 +1,32 @@ +/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- + * + * Copyright © 2023–2024 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.bean; + +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.TestInstancePreConstructCallback; +import org.junit.jupiter.api.extension.TestInstanceFactoryContext; + +public class ExperimentalPreConstructCallback implements TestInstancePreConstructCallback { + + public ExperimentalPreConstructCallback() { + super(); + } + + @Override + public final void preConstructTestInstance(final TestInstanceFactoryContext tifc, + final ExtensionContext ec) { + System.out.println("*** preConstructTestInstance called"); + } + +} diff --git a/src/test/java/org/microbean/bean/ExperimentalTestInstancePostProcessor.java b/src/test/java/org/microbean/bean/ExperimentalTestInstancePostProcessor.java new file mode 100644 index 0000000..375508b --- /dev/null +++ b/src/test/java/org/microbean/bean/ExperimentalTestInstancePostProcessor.java @@ -0,0 +1,30 @@ +/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- + * + * Copyright © 2024 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.bean; + +import org.junit.jupiter.api.extension.ExtensionContext; +import org.junit.jupiter.api.extension.TestInstancePostProcessor; + +public class ExperimentalTestInstancePostProcessor implements TestInstancePostProcessor { + + public ExperimentalTestInstancePostProcessor() { + super(); + } + + public final void postProcessTestInstance(final Object testInstance, + final ExtensionContext extensionContext) { + System.out.println("*** postProcessTestInstance called"); + } + +} diff --git a/src/test/java/org/microbean/bean/TestAssignability.java b/src/test/java/org/microbean/bean/TestAssignability.java new file mode 100644 index 0000000..db81445 --- /dev/null +++ b/src/test/java/org/microbean/bean/TestAssignability.java @@ -0,0 +1,111 @@ +/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- + * + * Copyright © 2024 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.bean; + +import java.io.Serializable; + +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; + +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.IntersectionType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.TypeVariable; +import javax.lang.model.type.WildcardType; + +import org.junit.jupiter.api.Test; + +import org.microbean.construct.DefaultDomain; +import org.microbean.construct.Domain; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +final class TestAssignability { + + private static final Domain domain = new DefaultDomain(); + + private TestAssignability() { + super(); + } + + @Test + final void testAssignabilityOfTypeVariable() throws NoSuchMethodException { + final ExecutableElement ee = domain.executableElement(domain.typeElement(this.getClass().getName()), + domain.noType(TypeKind.VOID), + "testAssignabilityOfTypeVariable"); + final TypeVariable t = (TypeVariable)domain.typeParameterElement(ee, "T").asType(); + final TypeMirror s = domain.typeParameterElement(ee, "S").asType(); + final TypeMirror r = domain.typeParameterElement(ee, "S").asType(); + final DeclaredType integer = domain.declaredType("java.lang.Integer"); + final DeclaredType serializable = domain.declaredType("java.io.Serializable"); + final DeclaredType number = domain.declaredType("java.lang.Number"); + + // + // IMPORTANT: + // + // Remember that TypeAndElementSource#assignable(), like javax.lang.model.util.Types#isAssignable() on which it is + // based, is "backwards"! + // + + // integer is the *payload*; number is the *receiver*; integer is assignable to number + assertTrue(domain.assignable(integer, number)); + assertTrue(domain.assignable(integer, serializable)); + assertTrue(domain.assignable(number, serializable)); + + // t is the *payload*; integer is the *receiver*; T is assignable to Integer + assertTrue(domain.assignable(t, integer)); + assertTrue(domain.assignable(t, serializable)); + + // t has multiple bounds so its upper bound is an intersection type + final IntersectionType tUpper = (IntersectionType)t.getUpperBound(); + assertSame(TypeKind.INTERSECTION, tUpper.getKind()); + + // t's upper bound is an intersection type, one of whose components is Integer; it is therefore assignable to integer + assertTrue(domain.assignable(tUpper, integer)); + assertTrue(domain.assignable(tUpper, serializable)); + + // t is the *payload*; number is the *receiver*; T is assignable to Number (by transitivity) + assertTrue(domain.assignable(t, number)); + + // t and s are not assignable in either direction + assertFalse(domain.assignable(t, s)); + assertFalse(domain.assignable(s, t)); + + // r is the *payload*; s is the *receiver*; R is assignable to S (it extends it) + assertTrue(domain.assignable(r, s)); + } + + @Test + final void testAssignabilityOfWildcard() { + final DeclaredType string = domain.declaredType("java.lang.String"); + final WildcardType qExtendsString = domain.wildcardType(string, null); + final DeclaredType extendsBound = (DeclaredType)qExtendsString.getExtendsBound(); + assertSame(TypeKind.DECLARED, extendsBound.getKind()); + assertNotSame(string, extendsBound); // or could be; can't rely on it + assertEquals("java.lang.String", ((TypeElement)extendsBound.asElement()).getQualifiedName().toString()); + assertTrue(domain.sameType(string, extendsBound)); + assertEquals(string, extendsBound); + + // Perhaps these assertions are surprising but they are correct. + assertFalse(domain.assignable(string, qExtendsString)); + assertFalse(domain.assignable(qExtendsString, string)); + } + +} diff --git a/src/test/java/org/microbean/bean/TestBean.java b/src/test/java/org/microbean/bean/TestBean.java index 8b02874..fe8211e 100644 --- a/src/test/java/org/microbean/bean/TestBean.java +++ b/src/test/java/org/microbean/bean/TestBean.java @@ -1,6 +1,6 @@ /* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- * - * Copyright © 2023–2024 microBean™. + * Copyright © 2023–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 @@ -14,17 +14,16 @@ package org.microbean.bean; import java.lang.constant.Constable; -import java.lang.constant.ConstantDesc; import java.util.List; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.microbean.constant.Constables; -import org.microbean.lang.Lang; -import org.microbean.lang.SameTypeEquality; -import org.microbean.lang.TypeAndElementSource; +import org.microbean.construct.DefaultDomain; +import org.microbean.construct.Domain; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; @@ -35,18 +34,20 @@ final class TestBean { - private static final TypeAndElementSource tes = Lang.typeAndElementSource(); + private static final Domain domain = new DefaultDomain(); private TestBean() { super(); } + @Disabled // types are not currently Constable @Test final void testConstableStuff() { final Factory f = new Constant<>("Hello"); final Id id = - new Id(List.of(tes.declaredType(String.class), - tes.declaredType(Object.class)), + new Id(domain, + List.of(domain.declaredType("java.lang.String"), + domain.javaLangObject().asType()), anyAndDefaultQualifiers(), SINGLETON_ID); assertTrue(id instanceof Constable); diff --git a/src/test/java/org/microbean/bean/TestBeanSelection.java b/src/test/java/org/microbean/bean/TestBeanSelection.java index cf44cfa..596e9f0 100644 --- a/src/test/java/org/microbean/bean/TestBeanSelection.java +++ b/src/test/java/org/microbean/bean/TestBeanSelection.java @@ -1,6 +1,6 @@ /* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- * - * Copyright © 2023–2024 microBean™. + * Copyright © 2023–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 @@ -22,21 +22,20 @@ import org.junit.jupiter.api.Test; -import org.microbean.lang.TypeAndElementSource; +import org.microbean.construct.Domain; +import org.microbean.construct.DefaultDomain; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.microbean.lang.Lang.typeAndElementSource; - import static org.microbean.scope.Scope.NONE_ID; final class TestBeanSelection { - private static final TypeAndElementSource tes = typeAndElementSource(); + private static final Domain domain = new DefaultDomain(); private static final Matcher matcher = - new IdMatcher(new QualifiersMatcher(), new InterceptorBindingsMatcher(), new TypeMatcher(tes)); + new IdMatcher(new BeanQualifiersMatcher(), new InterceptorBindingsMatcher(), new BeanTypeMatcher(domain)); private TestBeanSelection() { super(); @@ -53,56 +52,62 @@ final void testCDI_502_CDI_823() { // // ...even though it does not compile. (Go ahead; uncomment the snippet above and see for yourself.) final AttributedType t = - new AttributedType(tes.declaredType(tes.typeElement(List.class), - tes.declaredType(tes.typeElement(Optional.class), - tes.wildcardType(tes.declaredType(Object.class), - null)))); - assertTrue(matcher.test(t, new Id(List.of(tes.declaredType(tes.typeElement(List.class), - tes.declaredType(tes.typeElement(Optional.class), - tes.declaredType(Object.class)))), + new AttributedType(domain.declaredType(domain.typeElement("java.util.List"), + domain.declaredType(domain.typeElement("java.util.Optional"), + domain.wildcardType(domain.javaLangObject().asType(), + null)))); + assertTrue(matcher.test(t, new Id(domain, + List.of(domain.declaredType(domain.typeElement("java.util.List"), + domain.declaredType(domain.typeElement("java.util.Optional"), + domain.javaLangObject().asType()))), List.of(), NONE_ID))); } @Test final void testStringSelectsString() { - final AttributedType t = new AttributedType(tes.declaredType(String.class)); - assertTrue(matcher.test(t, new Id(List.of(tes.declaredType(String.class)), + final AttributedType t = new AttributedType(domain.typeElement("java.lang.String").asType()); + assertTrue(matcher.test(t, new Id(domain, + List.of(domain.typeElement("java.lang.String").asType()), List.of(), NONE_ID))); } @Test final void testStringDoesNotSelectObject() { - final AttributedType t = new AttributedType(tes.declaredType(String.class)); - assertFalse(matcher.test(t, new Id(List.of(tes.declaredType(Object.class)), + final AttributedType t = new AttributedType(domain.typeElement("java.lang.String").asType()); + assertFalse(matcher.test(t, new Id(domain, + List.of(domain.javaLangObject().asType()), List.of(), NONE_ID))); } @Test final void testIntSelectsInteger() { - final AttributedType t = new AttributedType(tes.primitiveType(TypeKind.INT)); - assertTrue(matcher.test(t, new Id(List.of(tes.declaredType(Integer.class)), + final AttributedType t = new AttributedType(domain.primitiveType(TypeKind.INT)); + assertTrue(matcher.test(t, new Id(domain, + List.of(domain.typeElement("java.lang.Integer").asType()), List.of(), NONE_ID))); } @Test final void testObjectDoesNotSelectString() { - final AttributedType t = new AttributedType(tes.declaredType(Object.class)); - assertFalse(matcher.test(t, new Id(List.of(tes.declaredType(String.class)), + final AttributedType t = new AttributedType(domain.javaLangObject().asType()); + assertFalse(matcher.test(t, new Id(domain, + List.of(domain.declaredType("java.lang.String")), List.of(), NONE_ID))); } @Test final void testListUnknownExtendsStringSelectsListString() { - final AttributedType t = new AttributedType(tes.declaredType(tes.typeElement(List.class), - tes.wildcardType(tes.declaredType(String.class), - null))); - assertTrue(matcher.test(t, new Id(List.of(tes.declaredType(tes.typeElement(List.class), - tes.declaredType(String.class))), + final AttributedType t = new AttributedType(domain.declaredType(domain.typeElement("java.util.List"), + domain.wildcardType(domain.declaredType("java.lang.String"), + null))); + assertTrue(matcher.test(t, new Id(domain, + List.of(domain.declaredType(domain.typeElement("java.util.List"), + domain.typeElement("java.lang.String").asType())), List.of(), NONE_ID))); } diff --git a/src/test/java/org/microbean/bean/TestBeanTypes.java b/src/test/java/org/microbean/bean/TestBeanTypes.java index 07cc617..5750fad 100644 --- a/src/test/java/org/microbean/bean/TestBeanTypes.java +++ b/src/test/java/org/microbean/bean/TestBeanTypes.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 @@ -21,21 +21,18 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.microbean.lang.TypeAndElementSource; - -import org.microbean.lang.type.DelegatingTypeMirror; +import org.microbean.construct.DefaultDomain; +import org.microbean.construct.Domain; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.microbean.bean.BeanTypes.name; - -import static org.microbean.lang.Lang.typeAndElementSource; +// import static org.microbean.bean.BeanTypes.name; final class TestBeanTypes { - private TypeAndElementSource tes; + private Domain domain; private BeanTypes beanTypes; @@ -45,166 +42,168 @@ private TestBeanTypes() { @BeforeEach final void setup() { - this.tes = typeAndElementSource(); - this.beanTypes = new BeanTypes(this.tes); + this.domain = new DefaultDomain(); + this.beanTypes = new BeanTypes(this.domain); } @Test final void testDirectSupertypesOfintAreEmpty() { - final List ts = tes.directSupertypes(tes.primitiveType(TypeKind.INT)); + final List ts = domain.directSupertypes(domain.primitiveType(TypeKind.INT)); assertTrue(ts.isEmpty()); // although note JLS 4.10.1; oh well } @Test final void testSupertypesOfint() { - final List ts = beanTypes.supertypes(tes.primitiveType(TypeKind.INT)); + final List ts = beanTypes.supertypes(domain.primitiveType(TypeKind.INT)); assertEquals(1, ts.size()); - assertEquals("int", name(ts.get(0))); + assertEquals("int", beanTypes.name(ts.get(0))); } @Test final void testBeanTypesOfint() { - final List ts = beanTypes.beanTypes(tes.primitiveType(TypeKind.INT)); + final List ts = beanTypes.beanTypes(domain.primitiveType(TypeKind.INT)); assertEquals(2, ts.size()); - assertEquals("int", name(ts.get(0))); - assertEquals("java.lang.Object", name(ts.get(1))); // per CDI specification + assertEquals("int", beanTypes.name(ts.get(0))); + assertEquals("java.lang.Object", beanTypes.name(ts.get(1))); // per CDI specification } @Test final void testDirectSupertypesOfObjectArrayAreASingleIntersectionType() { - final List ts = tes.directSupertypes(tes.arrayTypeOf(tes.declaredType("java.lang.Object"))); + final List ts = domain.directSupertypes(domain.arrayTypeOf(domain.declaredType("java.lang.Object"))); assertEquals(1, ts.size()); - assertEquals("java.io.Serializable&java.lang.Cloneable", name(ts.get(0))); // wut + assertEquals("java.io.Serializable&java.lang.Cloneable", beanTypes.name(ts.get(0))); // wut } @Test final void testSupertypesOfObjectArray() { - final List ts = beanTypes.supertypes(tes.arrayTypeOf(tes.declaredType("java.lang.Object"))); + final List ts = beanTypes.supertypes(domain.arrayTypeOf(domain.declaredType("java.lang.Object"))); assertEquals(5, ts.size()); - assertEquals("java.lang.Object[]", name(ts.get(0))); - assertEquals("java.io.Serializable&java.lang.Cloneable", name(ts.get(1))); // wut - assertEquals("java.lang.Object", name(ts.get(2))); - assertEquals("java.io.Serializable", name(ts.get(3))); - assertEquals("java.lang.Cloneable", name(ts.get(4))); + assertEquals("java.lang.Object[]", beanTypes.name(ts.get(0))); + assertEquals("java.io.Serializable&java.lang.Cloneable", beanTypes.name(ts.get(1))); // wut + assertEquals("java.lang.Object", beanTypes.name(ts.get(2))); + assertEquals("java.io.Serializable", beanTypes.name(ts.get(3))); + assertEquals("java.lang.Cloneable", beanTypes.name(ts.get(4))); } @Test final void testBeanTypesOfObjectArray() { - final List ts = beanTypes.beanTypes(tes.arrayTypeOf(tes.declaredType("java.lang.Object"))); + final List ts = beanTypes.beanTypes(domain.arrayTypeOf(domain.declaredType("java.lang.Object"))); assertEquals(2, ts.size()); - assertEquals("java.lang.Object[]", name(ts.get(0))); - assertEquals("java.lang.Object", name(ts.get(1))); + assertEquals("java.lang.Object[]", beanTypes.name(ts.get(0))); + assertEquals("java.lang.Object", beanTypes.name(ts.get(1))); } @Test final void testSupertypesOfString() { - final List ts = beanTypes.supertypes(tes.declaredType("java.lang.String")); + final List ts = beanTypes.supertypes(domain.declaredType("java.lang.String")); assertEquals(7, ts.size(), ts.toString()); - assertEquals("java.lang.String", name(ts.get(0))); - assertEquals("java.lang.Object", name(ts.get(1))); - assertEquals("java.io.Serializable", name(ts.get(2))); - assertEquals("java.lang.CharSequence", name(ts.get(3))); - assertEquals("java.lang.Comparable", name(ts.get(4))); - assertEquals("java.lang.constant.Constable", name(ts.get(5))); - assertEquals("java.lang.constant.ConstantDesc", name(ts.get(6))); + assertEquals("java.lang.String", beanTypes.name(ts.get(0))); + assertEquals("java.lang.Object", beanTypes.name(ts.get(1))); + assertEquals("java.io.Serializable", beanTypes.name(ts.get(2))); + assertEquals("java.lang.CharSequence", beanTypes.name(ts.get(3))); + assertEquals("java.lang.Comparable", beanTypes.name(ts.get(4))); + assertEquals("java.lang.constant.Constable", beanTypes.name(ts.get(5))); + assertEquals("java.lang.constant.ConstantDesc", beanTypes.name(ts.get(6))); } @Test final void testBeanTypesOfString() { - final List ts = beanTypes.beanTypes(tes.declaredType("java.lang.String")); + final List ts = beanTypes.beanTypes(domain.declaredType("java.lang.String")); assertEquals(7, ts.size(), ts.toString()); - assertEquals("java.lang.String", name(ts.get(0))); - assertEquals("java.lang.Object", name(ts.get(1))); - assertEquals("java.io.Serializable", name(ts.get(2))); - assertEquals("java.lang.CharSequence", name(ts.get(3))); - assertEquals("java.lang.Comparable", name(ts.get(4))); - assertEquals("java.lang.constant.Constable", name(ts.get(5))); - assertEquals("java.lang.constant.ConstantDesc", name(ts.get(6))); + assertEquals("java.lang.String", beanTypes.name(ts.get(0))); + assertEquals("java.lang.Object", beanTypes.name(ts.get(1))); + assertEquals("java.io.Serializable", beanTypes.name(ts.get(2))); + assertEquals("java.lang.CharSequence", beanTypes.name(ts.get(3))); + assertEquals("java.lang.Comparable", beanTypes.name(ts.get(4))); + assertEquals("java.lang.constant.Constable", beanTypes.name(ts.get(5))); + assertEquals("java.lang.constant.ConstantDesc", beanTypes.name(ts.get(6))); } @Test final void testDirectSupertypesOfInterfaceA() { - final List ts = tes.directSupertypes(tes.declaredType("org.microbean.bean.TestBeanTypes.A")); + final List ts = domain.directSupertypes(domain.declaredType("org.microbean.bean.TestBeanTypes.A")); assertEquals(1, ts.size()); assertEquals("java.lang.Object", ts.get(0).toString()); } @Test final void testDirectSupertypesOfInterfaceBExtendsA() { - final List ts = tes.directSupertypes(tes.declaredType("org.microbean.bean.TestBeanTypes.B")); + final List ts = domain.directSupertypes(domain.declaredType("org.microbean.bean.TestBeanTypes.B")); assertEquals(2, ts.size()); - assertEquals("java.lang.Object", name(ts.get(0))); - assertEquals("org.microbean.bean.TestBeanTypes.A", name(ts.get(1))); + assertEquals("java.lang.Object", beanTypes.name(ts.get(0))); + assertEquals("org.microbean.bean.TestBeanTypes.A", beanTypes.name(ts.get(1))); } @Test final void testDirectSupertypesOfInterfaceCExtendsBAreInDeclarationOrder() { - final List ts = tes.directSupertypes(tes.declaredType("org.microbean.bean.TestBeanTypes.C")); + final List ts = domain.directSupertypes(domain.declaredType("org.microbean.bean.TestBeanTypes.C")); assertEquals(3, ts.size()); - assertEquals("java.lang.Object", name(ts.get(0))); - assertEquals("org.microbean.bean.TestBeanTypes.A", name(ts.get(1))); - assertEquals("org.microbean.bean.TestBeanTypes.B", name(ts.get(2))); + assertEquals("java.lang.Object", beanTypes.name(ts.get(0))); + assertEquals("org.microbean.bean.TestBeanTypes.A", beanTypes.name(ts.get(1))); + assertEquals("org.microbean.bean.TestBeanTypes.B", beanTypes.name(ts.get(2))); } @Test final void testSupertypesOfListExtendsString() { final List ts = - beanTypes.supertypes(tes.declaredType(tes.typeElement("java.util.List"), - tes.wildcardType(tes.declaredType("java.lang.String"), + beanTypes.supertypes(domain.declaredType(domain.typeElement("java.util.List"), + domain.wildcardType(domain.declaredType("java.lang.String"), null))); assertEquals(5, ts.size()); - assertEquals("java.lang.Object", name(ts.get(0))); - assertEquals("java.util.List", name(ts.get(1))); - assertEquals("java.util.SequencedCollection", name(ts.get(2))); - assertEquals("java.util.Collection", name(ts.get(3))); - assertEquals("java.lang.Iterable", name(ts.get(4))); + assertEquals("java.lang.Object", beanTypes.name(ts.get(0))); + assertEquals("java.util.List", beanTypes.name(ts.get(1))); + assertEquals("java.util.SequencedCollection", beanTypes.name(ts.get(2))); + assertEquals("java.util.Collection", beanTypes.name(ts.get(3))); + assertEquals("java.lang.Iterable", beanTypes.name(ts.get(4))); } @Test final void testBeanTypesOfListExtendsString() { - final List ts = beanTypes.beanTypes(tes.declaredType(tes.typeElement("java.util.List"), - tes.wildcardType(tes.declaredType("java.lang.String"), + final List ts = beanTypes.beanTypes(domain.declaredType(domain.typeElement("java.util.List"), + domain.wildcardType(domain.declaredType("java.lang.String"), null))); assertEquals(1, ts.size()); - assertEquals("java.lang.Object", name(ts.get(0))); + assertEquals("java.lang.Object", beanTypes.name(ts.get(0))); } @Test final void testSupertypesOfWildcard() { - final TypeMirror t = tes.wildcardType(tes.declaredType("java.lang.String"), null); - final List ts = beanTypes.supertypes(t); + final TypeMirror t = domain.wildcardType(domain.declaredType("java.lang.String"), null); + final List ts = beanTypes.supertypes(t); assertEquals(1, ts.size()); // no direct supertypes assertSame(t, ts.get(0)); } @Test final void testBeanTypesOfWildcard() { - assertTrue(beanTypes.beanTypes(tes.wildcardType(tes.declaredType("java.lang.String"), null)).isEmpty()); + assertTrue(beanTypes.beanTypes(domain.wildcardType(domain.declaredType("java.lang.String"), null)).isEmpty()); } + /* @Test final void testSupertypesOfTypeVariable() throws ReflectiveOperationException { final List ts = - beanTypes.supertypes(tes.typeVariable(this.getClass().getDeclaredMethod("testSupertypesOfTypeVariable").getTypeParameters()[0])); + beanTypes.supertypes(domain.typeVariable(this.getClass().getDeclaredMethod("testSupertypesOfTypeVariable").getTypeParameters()[0])); assertEquals(2, ts.size()); - assertEquals("T", name(ts.get(0))); - assertEquals("java.lang.Object", name(ts.get(1))); + assertEquals("T", beanTypes.name(ts.get(0))); + assertEquals("java.lang.Object", beanTypes.name(ts.get(1))); } @Test final void testBeanTypesOfTypeVariable() throws ReflectiveOperationException { - final List ts = beanTypes.beanTypes(tes.typeVariable(this.getClass().getDeclaredMethod("testBeanTypesOfTypeVariable").getTypeParameters()[0])); + final List ts = beanTypes.beanTypes(domain.typeVariable(this.getClass().getDeclaredMethod("testBeanTypesOfTypeVariable").getTypeParameters()[0])); assertEquals(1, ts.size()); - assertEquals("java.lang.Object", name(ts.get(0))); + assertEquals("java.lang.Object", beanTypes.name(ts.get(0))); } + */ private static interface A {}; diff --git a/src/test/java/org/microbean/bean/TestConstableSemantics.java b/src/test/java/org/microbean/bean/TestConstableSemantics.java index 97cc24e..0b0fb81 100644 --- a/src/test/java/org/microbean/bean/TestConstableSemantics.java +++ b/src/test/java/org/microbean/bean/TestConstableSemantics.java @@ -1,6 +1,6 @@ /* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- * - * Copyright © 2023 microBean™. + * Copyright © 2023–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 @@ -29,13 +29,13 @@ import java.util.TreeSet; import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; import org.microbean.constant.Constables; -import org.microbean.lang.Lang; -import org.microbean.lang.SameTypeEquality; -import org.microbean.lang.TypeAndElementSource; +import org.microbean.construct.DefaultDomain; +import org.microbean.construct.Domain; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; @@ -53,15 +53,15 @@ final class TestConstableSemantics { - private static TypeAndElementSource tes; - + private static Domain domain; + private TestConstableSemantics() { super(); } @BeforeAll - static final void initializeTes() { - tes = Lang.typeAndElementSource(); + static final void initializeDomain() { + domain = new DefaultDomain(); } @Test @@ -70,27 +70,17 @@ final void testAnyAndDefaultQualifiersList() throws ReflectiveOperationException assertEquals(list, Constables.describeConstable(list).orElseThrow().resolveConstantDesc(MethodHandles.lookup())); } + @Disabled // Types are not currently Constable @Test final void testId() throws ReflectiveOperationException { final Id id = - new Id(List.of(tes.declaredType(String.class), tes.declaredType(Object.class)), + new Id(domain, + List.of(domain.typeElement("java.lang.String").asType(), domain.javaLangObject().asType()), anyAndDefaultQualifiers(), SINGLETON_ID); assertEquals(id, Constables.describeConstable(id).orElseThrow().resolveConstantDesc(lookup())); } - /* - @Test - final void testFactory() throws ReflectiveOperationException { - final Factory f = new Singleton<>("Hello"); - assertNull(f.singleton()); - @SuppressWarnings("unchecked") - final Factory f2 = (Factory)Constables.describeConstable(f).orElseThrow().resolveConstantDesc(lookup()); - assertNotSame(f, f2); - assertSame(f.singleton(), f2.singleton()); - } - */ - @Test final void testConstant() throws ReflectiveOperationException { final Constant c = new Constant<>("Hello"); diff --git a/src/test/java/org/microbean/bean/TestContains.java b/src/test/java/org/microbean/bean/TestContains.java new file mode 100644 index 0000000..960ebb0 --- /dev/null +++ b/src/test/java/org/microbean/bean/TestContains.java @@ -0,0 +1,118 @@ +/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- + * + * Copyright © 2024 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.bean; + +import java.io.Serializable; + +import javax.lang.model.element.ExecutableElement; +import javax.lang.model.element.TypeElement; +import javax.lang.model.element.TypeParameterElement; + +import javax.lang.model.type.DeclaredType; +import javax.lang.model.type.IntersectionType; +import javax.lang.model.type.TypeKind; +import javax.lang.model.type.TypeMirror; +import javax.lang.model.type.TypeVariable; +import javax.lang.model.type.WildcardType; + +import org.junit.jupiter.api.Test; + +import org.microbean.construct.DefaultDomain; +import org.microbean.construct.Domain; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotSame; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +final class TestContains { + + private static final Domain domain = new DefaultDomain(); + + private TestContains() { + super(); + } + + @Test + final void testWildcardContains() { + + final ExecutableElement ee = domain.executableElement(domain.typeElement(this.getClass().getName()), + domain.noType(TypeKind.VOID), + "testWildcardContains"); + final TypeMirror t = domain.typeParameterElement(ee, "T").asType(); + final TypeMirror s = domain.typeParameterElement(ee, "S").asType(); + final DeclaredType string = domain.declaredType("java.lang.String"); + final DeclaredType charSequence = domain.declaredType("java.lang.CharSequence"); + final DeclaredType object = domain.declaredType("java.lang.Object"); + final WildcardType qExtendsCharSequence = domain.wildcardType(charSequence, null); + final WildcardType qExtendsString = domain.wildcardType(string, null); + final WildcardType qSuperCharSequence = domain.wildcardType(null, charSequence); + final WildcardType qSuperString = domain.wildcardType(null, string); + + assertTrue(domain.contains(string, string)); // reflexive + assertTrue(domain.contains(qExtendsString, qExtendsString)); // reflexive + assertTrue(domain.contains(qExtendsString, string)); + assertTrue(domain.contains(qExtendsCharSequence, charSequence)); + assertTrue(domain.contains(qExtendsCharSequence, string)); + assertTrue(domain.contains(qExtendsCharSequence, qExtendsString)); + + assertTrue(domain.contains(qExtendsCharSequence, t)); + assertTrue(domain.contains(qExtendsCharSequence, s)); + assertTrue(domain.contains(qExtendsString, t)); + assertTrue(domain.contains(qExtendsString, s)); + + assertTrue(domain.contains(qSuperString, qSuperString)); // reflexive + assertTrue(domain.contains(qSuperString, string)); + assertTrue(domain.contains(qSuperString, charSequence)); + assertTrue(domain.contains(qSuperString, qSuperCharSequence)); + assertTrue(domain.contains(qSuperCharSequence, charSequence)); + assertTrue(domain.contains(qSuperCharSequence, object)); + + assertFalse(domain.contains(qSuperCharSequence, string)); + assertFalse(domain.contains(string, charSequence)); + assertFalse(domain.contains(charSequence, string)); + assertFalse(domain.contains(charSequence, qExtendsCharSequence)); + assertFalse(domain.contains(string, qExtendsString)); + assertFalse(domain.contains(string, qSuperString)); + assertFalse(domain.contains(qExtendsString, qSuperString)); + assertFalse(domain.contains(qSuperString, qExtendsString)); + } + + @Test + final void testTypeVariableContainsNothingExceptItself() { + final ExecutableElement ee = domain.executableElement(domain.typeElement(this.getClass().getName()), + domain.noType(TypeKind.VOID), + "testTypeVariableContainsNothingExceptItself"); + final TypeMirror t = domain.typeParameterElement(ee, "T").asType(); + final TypeVariable s = (TypeVariable)domain.typeParameterElement(ee, "S").asType(); + final TypeMirror r = domain.typeParameterElement(ee, "R").asType(); + final DeclaredType integer = domain.declaredType("java.lang.Integer"); + + assertTrue(domain.contains(t, t)); + + assertFalse(domain.contains(t, integer)); + assertFalse(domain.contains(integer, t)); + assertFalse(domain.contains(s, s.getUpperBound())); + assertFalse(domain.contains(s.getUpperBound(), s)); + assertFalse(domain.contains(t, s)); + assertFalse(domain.contains(s, t)); + assertFalse(domain.contains(s, r)); + assertFalse(domain.contains(r, s)); + assertFalse(domain.contains(t, r)); + assertFalse(domain.contains(r, t)); + } + +} diff --git a/src/test/java/org/microbean/bean/TestFactory.java b/src/test/java/org/microbean/bean/TestFactory.java index 881e4bb..8e4ce7f 100644 --- a/src/test/java/org/microbean/bean/TestFactory.java +++ b/src/test/java/org/microbean/bean/TestFactory.java @@ -22,7 +22,7 @@ import static org.junit.jupiter.api.Assertions.assertSame; import static org.junit.jupiter.api.Assertions.assertTrue; -final class TestFactory { +final class TestFactory { private TestFactory() { super(); diff --git a/src/test/java/org/microbean/bean/TestJUnitExtensionsPerClass.java b/src/test/java/org/microbean/bean/TestJUnitExtensionsPerClass.java new file mode 100644 index 0000000..8f3e282 --- /dev/null +++ b/src/test/java/org/microbean/bean/TestJUnitExtensionsPerClass.java @@ -0,0 +1,46 @@ +/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- + * + * Copyright © 2024 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.bean; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; + +import org.junit.jupiter.api.extension.ExtendWith; + +import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS; +import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_METHOD; + +@ExtendWith(ExperimentalPreConstructCallback.class) +@ExtendWith(ExperimentalBeforeAllCallback.class) +@ExtendWith(ExperimentalTestInstancePostProcessor.class) +@ExtendWith(ExperimentalBeforeEachCallback.class) +@TestInstance(PER_CLASS) +final class TestJUnitExtensionsPerClass { + + private TestJUnitExtensionsPerClass() { + super(); + System.out.println("*** per class constructor"); + } + + @Test + final void frob() { + System.out.println("*** frob"); + } + + @Test + final void blat() { + System.out.println("*** blat"); + } + +} diff --git a/src/test/java/org/microbean/bean/TestJUnitExtensionsPerMethod.java b/src/test/java/org/microbean/bean/TestJUnitExtensionsPerMethod.java new file mode 100644 index 0000000..4475c50 --- /dev/null +++ b/src/test/java/org/microbean/bean/TestJUnitExtensionsPerMethod.java @@ -0,0 +1,46 @@ +/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- + * + * Copyright © 2024 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.bean; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.TestInstance; + +import org.junit.jupiter.api.extension.ExtendWith; + +import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_CLASS; +import static org.junit.jupiter.api.TestInstance.Lifecycle.PER_METHOD; + +@ExtendWith(ExperimentalPreConstructCallback.class) +@ExtendWith(ExperimentalBeforeAllCallback.class) +@ExtendWith(ExperimentalTestInstancePostProcessor.class) +@ExtendWith(ExperimentalBeforeEachCallback.class) +@TestInstance(PER_METHOD) +final class TestJUnitExtensionsPerMethod { + + private TestJUnitExtensionsPerMethod() { + super(); + System.out.println("*** per method constructor"); + } + + @Test + final void frob() { + System.out.println("*** frob"); + } + + @Test + final void blat() { + System.out.println("*** blat"); + } + +} diff --git a/src/test/java/org/microbean/bean/TestWeldEventDelivery.java b/src/test/java/org/microbean/bean/TestWeldEventDelivery.java new file mode 100644 index 0000000..df75301 --- /dev/null +++ b/src/test/java/org/microbean/bean/TestWeldEventDelivery.java @@ -0,0 +1,122 @@ +/* -*- mode: Java; c-basic-offset: 2; indent-tabs-mode: nil; coding: utf-8-unix -*- + * + * Copyright © 2024 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.bean; + +import java.util.Set; + +import jakarta.enterprise.context.spi.CreationalContext; + +import jakarta.enterprise.event.Event; +import jakarta.enterprise.event.Observes; + +import jakarta.enterprise.inject.se.SeContainer; +import jakarta.enterprise.inject.se.SeContainerInitializer; + +import jakarta.enterprise.inject.spi.BeanManager; +import jakarta.enterprise.inject.spi.Extension; +import jakarta.enterprise.inject.spi.InjectionPoint; +import jakarta.enterprise.inject.spi.InjectionTarget; +import jakarta.enterprise.inject.spi.ProcessBeanAttributes; +import jakarta.enterprise.inject.spi.ProcessInjectionTarget; + +import jakarta.inject.Inject; +import jakarta.inject.Singleton; + +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertSame; +import static org.junit.jupiter.api.Assertions.assertTrue; + +final class TestWeldEventDelivery { + + private SeContainer container; + + @Inject + private Event event; + + private boolean primitiveReceived; + + private boolean declaredReceived; + + private TestWeldEventDelivery() { + super(); + } + + @BeforeEach + final void resetState() { + primitiveReceived = false; + declaredReceived = false; + } + + @BeforeEach + final void startContainer() { + final Class testClass = this.getClass(); + final TestWeldEventDelivery testInstance = this; + this.container = SeContainerInitializer.newInstance() + .disableDiscovery() + .addBeanClasses(testClass) + .addExtensions(new Extension() { + // This ceremony is a nice way, finally, to get a generic JUnit test class beanified in the proper way. I may + // move this to a JUnit helper library someday. Left here for posterity. + final void forceSingletonScope(@Observes final ProcessBeanAttributes pba) { + if (pba.getAnnotated().getBaseType() == testClass) { + pba.configureBeanAttributes().scope(Singleton.class); + } + } + final void beanifyTestInstance(@Observes + final ProcessInjectionTarget pit, + final BeanManager bm) { + if (pit.getAnnotatedType().getJavaClass() == testClass) { + final InjectionTarget it = pit.getInjectionTarget(); + pit.setInjectionTarget(new InjectionTarget<>() { + @Override public final void dispose(final T i) { it.dispose(i); } + @Override public final Set getInjectionPoints() { return it.getInjectionPoints(); } + @Override public final void inject(final T i, final CreationalContext cc) { it.inject(i, cc); } + @Override public final void postConstruct(final T i) { it.postConstruct(i); } + @Override public final void preDestroy(final T i) { it.preDestroy(i); } + @Override @SuppressWarnings("unchecked") public final T produce(final CreationalContext cc) { return (T)testInstance; } + }); + } + } + }) + .initialize(); + // Explicit selection is required to trigger injection. + assertSame(testInstance, this.container.select(testClass).get()); + } + + @AfterEach + final void stopContainer() { + this.container.close(); + } + + @Test + final void testWeldEventDelivery() { + this.event.fire(Integer.valueOf(42)); + assertTrue(this.primitiveReceived); + assertTrue(this.declaredReceived); + } + + private void primitiveEventReceived(@Observes final int event) { + this.primitiveReceived = true; + } + + private void wrapperEventReceived(@Observes final Integer event) { + assertNotNull(event); + this.declaredReceived = true; + } + +} diff --git a/src/test/java/org/microbean/bean/model/TestElements.java b/src/test/java/org/microbean/bean/model/TestElements.java index e050a3b..b6046ba 100644 --- a/src/test/java/org/microbean/bean/model/TestElements.java +++ b/src/test/java/org/microbean/bean/model/TestElements.java @@ -26,18 +26,17 @@ import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.Test; -import org.microbean.lang.element.DelegatingElement; +import org.microbean.construct.DefaultDomain; +import org.microbean.construct.Domain; -import org.microbean.lang.TypeAndElementSource; - -import static org.microbean.lang.Lang.typeAndElementSource; +import org.microbean.construct.element.UniversalElement; import static org.junit.jupiter.api.Assertions.assertTrue; import static org.junit.jupiter.api.Assertions.assertNotNull; final class TestElements { - private static final TypeAndElementSource tes = typeAndElementSource(); + private static final Domain domain = new DefaultDomain(); private TestElements() { super(); @@ -45,8 +44,8 @@ private TestElements() { @Test final void testStreamDepthFirst() { - final Element self = tes.typeElement(this.getClass().getName()); - assertTrue(self instanceof DelegatingElement); + final Element self = domain.typeElement(this.getClass().getName()); + assertTrue(self instanceof UniversalElement); Trees.streamDepthFirst(self, Elements::parametersAndEnclosedElements) .forEachOrdered(e -> System.out.println(e.getSimpleName() + " (" + e.getKind() + ")")); } @@ -54,7 +53,7 @@ final void testStreamDepthFirst() { @Disabled // This works fine, but takes a good long while so is disabled by default. @Test final void testBigHorkingStream() { - final Element javaBase = tes.moduleElement("java.base"); + final Element javaBase = domain.moduleElement("java.base"); assertNotNull(javaBase); Trees.streamDepthFirst(javaBase, Elements::parametersAndEnclosedElements) .forEachOrdered(e -> System.out.println(e.getSimpleName() + " (" + e.getKind() + ")"));